diff options
Diffstat (limited to 'src/user')
151 files changed, 0 insertions, 15993 deletions
diff --git a/src/user/app/drawmouse/drawmouse.c b/src/user/app/drawmouse/drawmouse.c deleted file mode 100644 index 31a1255..0000000 --- a/src/user/app/drawmouse/drawmouse.c +++ /dev/null @@ -1,87 +0,0 @@ -#include <camellia.h> -#include <camellia/syscalls.h> -#include <shared/ring.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <draw.h> - -#define MOUSE_SIZE 10 - -#define eprintf(fmt, ...) fprintf(stderr, "drawmouse: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - -struct framebuf fb, lastbehind; -struct rect dirty; - -static uint8_t r_buf[64]; -static ring_t r = {(void*)r_buf, sizeof r_buf, 0, 0}; -static struct {uint32_t x, y;} cursor, lastcur; - - -static void draw_mouse(void) { - static bool drawn = false; - if (drawn) - fb_cpy(&fb, &lastbehind, lastcur.x, lastcur.y, 0, 0, MOUSE_SIZE, MOUSE_SIZE); - drawn = true; - dirty_mark(&dirty, lastcur.x, lastcur.y); - dirty_mark(&dirty, lastcur.x + MOUSE_SIZE, lastcur.y + MOUSE_SIZE); - fb_cpy(&lastbehind, &fb, 0, 0, cursor.x, cursor.y, MOUSE_SIZE, MOUSE_SIZE); - for (int i = 0; i < MOUSE_SIZE; i++) { - for (int j = 0; j < MOUSE_SIZE - i; j++) { - uint32_t *px = fb_pixel(&fb, cursor.x + i, cursor.y + j); - if (px) { - *px ^= 0x808080; - dirty_mark(&dirty, cursor.x + i, cursor.y + j); - } - } - } - lastcur = cursor; -} - - -struct packet { - uint8_t left : 1; - uint8_t right : 1; - uint8_t middle : 1; - uint8_t _useless : 5; - int8_t dx, dy; -} __attribute__((packed)); - -int main(void) { - char buf[64]; - hid_t fd = camellia_open("/kdev/ps2/mouse", OPEN_READ); - if (fd < 0) { - eprintf("couldn't open mouse"); - return 1; - } - - if (fb_setup(&fb, "/kdev/video/") < 0) { - eprintf("fb_setup error"); - return 1; - } - fb_anon(&lastbehind, MOUSE_SIZE, MOUSE_SIZE); - - - for (;;) { - int len = _sys_read(fd, buf, sizeof buf, 0); - if (len == 0) break; - ring_put(&r, buf, len); - while (ring_used(&r) >= 3) { - struct packet p; - ring_get(&r, &p, sizeof p); - p.dy *= -1; - // TODO check mouse click - if (-p.dx > (int)cursor.x) p.dx = -cursor.x; - if (-p.dy > (int)cursor.y) p.dy = -cursor.y; - cursor.x += p.dx; - cursor.y += p.dy; - if (cursor.x >= fb.width) cursor.x = fb.width - 1; - if (cursor.y >= fb.height) cursor.y = fb.height - 1; - draw_mouse(); - if (p.left && p.right) return 0; - } - dirty_flush(&dirty, &fb); - } - return 0; -} diff --git a/src/user/app/dvd/dvd.c b/src/user/app/dvd/dvd.c deleted file mode 100644 index a15b440..0000000 --- a/src/user/app/dvd/dvd.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <camellia/execbuf.h> -#include <camellia/syscalls.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <draw.h> - -#define eprintf(fmt, ...) fprintf(stderr, "vterm: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - -struct framebuf fb; -struct rect dirty; - -void draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t col) { - for (uint32_t i = 0; i < w; i++) { - for (uint32_t j = 0; j < h; j++) { - *((uint32_t*)(fb.b + fb.pitch * (y+j) + 4 * (x+i))) = col; - } - } - dirty_mark(&dirty, x, y); - dirty_mark(&dirty, x + w, y + h); -} - -int main(void) { - if (fb_setup(&fb, "/kdev/video/") < 0) { - eprintf("fb_setup error"); - return 1; - } - int dx = 2, dy = 2, x = 100, y = 100, w = 150, h = 70; - uint32_t col = 0x800000; - - for (;;) { - if (x + dx < 0 || (size_t)(x + dx + w) >= fb.width) dx *= -1; - if (y + dy < 0 || (size_t)(y + dy + h) >= fb.height) dy *= -1; - x += dx; - y += dy; - draw_rect(x, y, w, h, col++); - dirty_flush(&dirty, &fb); - _sys_sleep(1000 / 60); - } - - return 1; -} diff --git a/src/user/app/ext2fs/ext2 b/src/user/app/ext2fs/ext2 deleted file mode 160000 -Subproject 8db7b4fbb2429b504d7c711dae89d917de16bed diff --git a/src/user/app/ext2fs/main.c b/src/user/app/ext2fs/main.c deleted file mode 100644 index 12ef3bc..0000000 --- a/src/user/app/ext2fs/main.c +++ /dev/null @@ -1,243 +0,0 @@ -#include "ext2/ex_cache.h" -#include "ext2/ext2.h" -#include <assert.h> -#include <camellia/flags.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> -#include <camellia/fsutil.h> -#include <camellia/syscalls.h> -#include <err.h> -#include <errno.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -struct handle { - uint32_t n; - bool dir; -}; - -static int my_read(void *fp, void *buf, size_t len, size_t off); -static int my_write(void *fp, const void *buf, size_t len, size_t off); -static void do_open(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf); -static void do_read(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf, size_t buflen); -static void do_write(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf); -static void do_getsize(struct ext2 *fs, hid_t reqh, struct ufs_request *req); - -static int -my_read(void *fp, void *buf, size_t len, size_t off) -{ - if (fseek(fp, off, SEEK_SET) < 0) { - return -1; - } else if (fread(buf, len, 1, fp) == 1) { - return 0; - } else { - return -1; - } -} - -static int -my_write(void *fp, const void *buf, size_t len, size_t off) -{ - if (fseek(fp, off, SEEK_SET) < 0) { - return -1; - } else if (fwrite(buf, len, 1, fp) == 1) { - return 0; - } else { - return -1; - } -} - -static void -do_open(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf) -{ - bool is_dir = req->len == 0 || buf[req->len-1] == '/'; - uint32_t n = ext2c_walk(fs, buf, req->len); - if (n == 0) { - if (is_dir) { - _sys_fs_respond(reqh, NULL, -ENOSYS, 0); - return; - } - /* buf[0] == '/', strrchr != NULL */ - char *name = strrchr(buf, '/') + 1; - uint32_t dir_n = ext2c_walk(fs, buf, name - buf); - if (dir_n == 0) { - _sys_fs_respond(reqh, NULL, -ENOENT, 0); - return; - } - n = ext2_alloc_inode(fs, 0100700); - if (n == 0) { - _sys_fs_respond(reqh, NULL, -1, 0); - return; - } - if (ext2_link(fs, dir_n, name, n, 1) < 0) { - _sys_fs_respond(reqh, NULL, -1, 0); - return; - } - } else { - struct ext2d_inode *inode = ext2_req_inode(fs, n); - if (!inode) { - _sys_fs_respond(reqh, NULL, -ENOENT, 0); - return; - } - int type = (inode->perms >> 12) & 0xF; - ext2_dropreq(fs, inode, false); - - if ((type == 0x8 && is_dir) || (type == 0x4 && !is_dir)) { - _sys_fs_respond(reqh, NULL, -ENOENT, 0); - return; - } else if (type != 0x8 && type != 0x4) { - _sys_fs_respond(reqh, NULL, -ENOSYS, 0); - return; - } - } - - struct handle *h = malloc(sizeof *h); - if (!h) { - _sys_fs_respond(reqh, NULL, -1, 0); - return; - } - h->n = n; - h->dir = is_dir; - _sys_fs_respond(reqh, h, 0, 0); -} - -static void -do_read(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf, size_t buflen) -{ - struct handle *h = req->id; - if (!h->dir) { - struct ext2d_inode *inode = ext2_req_inode(fs, h->n); - if (!inode) goto err; - fs_normslice(&req->offset, &req->capacity, inode->size_lower, false); - ext2_dropreq(fs, inode, false); - - void *b = ext2_req_file(fs, h->n, &req->capacity, req->offset); - if (b) { - _sys_fs_respond(reqh, b, req->capacity, 0); - ext2_dropreq(fs, b, false); - } else if (req->capacity == 0) { - /* set by ext2_req_file on EOF */ - _sys_fs_respond(reqh, b, 0, 0); - } else goto err; - } else { - struct dirbuild db; - char namebuf[257]; - if (req->capacity > buflen) - req->capacity = buflen; - dir_start(&db, req->offset, buf, buflen); - for (struct ext2_diriter iter = {0}; ext2_diriter(&iter, fs, h->n); ) { - if (iter.ent->namelen_lower == 1 && iter.ent->name[0] == '.') { - continue; - } - if (iter.ent->namelen_lower == 2 - && iter.ent->name[0] == '.' - && iter.ent->name[1] == '.') - { - continue; - } - if (iter.ent->type == 2) { /* dir */ - memcpy(namebuf, iter.ent->name, iter.ent->namelen_lower); - namebuf[iter.ent->namelen_lower] = '/'; - dir_appendl(&db, namebuf, iter.ent->namelen_lower + 1); - } else { - dir_appendl(&db, iter.ent->name, iter.ent->namelen_lower); - } - } - _sys_fs_respond(reqh, buf, dir_finish(&db), 0); - } - return; -err: - _sys_fs_respond(reqh, NULL, -1, 0); -} - -static void -do_write(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf) -{ - struct handle *h = req->id; - if (h->dir) goto err; - - struct ext2d_inode *inode = ext2_req_inode(fs, h->n); - if (!inode) goto err; - fs_normslice(&req->offset, &req->len, inode->size_lower, true); - if ((req->flags & WRITE_TRUNCATE) || inode->size_lower < req->offset + req->len) { - inode->size_lower = req->offset + req->len; - if (ext2_dropreq(fs, inode, true) < 0) { - goto err; - } - } else { - ext2_dropreq(fs, inode, false); - } - inode = NULL; - - int ret = ext2_write(fs, h->n, buf, req->len, req->offset); - _sys_fs_respond(reqh, NULL, ret, 0); - return; -err: - _sys_fs_respond(reqh, NULL, -1, 0); -} - -static void -do_getsize(struct ext2 *fs, hid_t reqh, struct ufs_request *req) { - struct handle *h = req->id; - if (h->dir) goto err; - - struct ext2d_inode *inode = ext2_req_inode(fs, h->n); - if (!inode) goto err; - _sys_fs_respond(reqh, NULL, inode->size_lower, 0); - ext2_dropreq(fs, inode, false); - return; -err: - _sys_fs_respond(reqh, NULL, -1, 0); -} - -int -main(int argc, char **argv) -{ - intr_set(NULL); - - if (argc < 2) errx(1, "bad usage"); - // TODO pread/pwrite for normal handles - FILE *disk = fopen(argv[1], "r+"); - if (!disk) err(1, "couldn't open '%s'", argv[1]); - - struct e2device *dev = exc_init(my_read, my_write, (void*)disk); - if (!dev) errx(1, "exc_init failed"); - struct ext2 *fs = ext2_opendev(dev, exc_req, exc_drop); - if (!fs) errx(1, "ext2_opendev failed"); - - const size_t buflen = 4096; - char *buf = malloc(buflen); - struct ufs_request req; - for (;;) { - hid_t reqh = ufs_wait(buf, buflen, &req); - struct handle *h = req.id; - if (reqh < 0) break; - switch (req.op) { - case VFSOP_OPEN: - do_open(fs, reqh, &req, buf); - break; - case VFSOP_READ: - do_read(fs, reqh, &req, buf, buflen); - break; - case VFSOP_WRITE: - do_write(fs, reqh, &req, buf); - break; - case VFSOP_GETSIZE: - do_getsize(fs, reqh, &req); - break; - case VFSOP_CLOSE: - free(h); - _sys_fs_respond(reqh, NULL, -1, 0); - break; - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - warnx("cleaning up"); - - return 1; -} diff --git a/src/user/app/find/find.c b/src/user/app/find/find.c deleted file mode 100644 index d473b82..0000000 --- a/src/user/app/find/find.c +++ /dev/null @@ -1,55 +0,0 @@ -#include <camellia/path.h> -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -void recurse(char *path) { - DIR *d = opendir(path); - if (!d) { - warn("couldn't open %s", path); - return; - } - for (;;) { - struct dirent *dent; - errno = 0; - dent = readdir(d); - if (!dent) { - if (errno) { - warn("when reading %s", path); - } - break; - } - printf("%s%s\n", path, dent->d_name); - /* if the string ends with '/' */ - if (strchr(dent->d_name, '\0')[-1] == '/') { - // TODO no overflow check - char *pend = strchr(path, '\0'); - strcpy(pend, dent->d_name); - recurse(path); - *pend = '\0'; - } - } - closedir(d); -} - -void find(const char *path) { - // TODO bound checking - // TODO or just implement asprintf() - char *buf = malloc(PATH_MAX); - memcpy(buf, path, strlen(path)+1); - recurse(buf); - free(buf); -} - -int main(int argc, char **argv) { - if (argc < 2) { - find("/"); - } else { - for (int i = 1; i < argc; i++) - find(argv[i]); - } - return 0; -} diff --git a/src/user/app/httpd/httpd.c b/src/user/app/httpd/httpd.c deleted file mode 100644 index 668e534..0000000 --- a/src/user/app/httpd/httpd.c +++ /dev/null @@ -1,77 +0,0 @@ -/* garbage httpd, just to see if it works - * easily DoSable (like the rest of the network stack), vulnerable to path traversal, etc */ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <err.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -static void handle(FILE *c) { - char buf[2048]; - fgets(buf, sizeof buf, c); - printf("%s", buf); - - if (memcmp(buf, "GET /", 5) != 0) { - fprintf(c, "HTTP/1.1 400 Bad Request\r\n\r\n"); - return; - } - char *path = buf + 4; - char *end = strchr(path, ' '); - if (end) *end = '\0'; - - hid_t h = _sys_open(path, strlen(path), OPEN_READ); - if (h < 0) { - fprintf(c, "HTTP/1.1 404 Not Found\r\n\r\n"); - return; - } - FILE *f = fdopen(h, "r"); - if (!f) { - fprintf(c, "HTTP/1.1 500 Internal Server Error\r\n\r\n"); - return; - } - - if (path[strlen(path) - 1] != '/') { - /* regular file */ - fprintf(c, "HTTP/1.1 200 OK\r\n"); - fprintf(c, "\r\n"); - for (;;) { - int len = fread(buf, 1, sizeof buf, f); - if (len <= 0) break; - fwrite(buf, 1, len, c); - } - } else { - /* directory listing */ - fprintf(c, "HTTP/1.1 200 OK\r\n"); - fprintf(c, "Content-Type: text/html; charset=UTF-8\r\n"); - fprintf(c, "\r\n"); - fprintf(c, "<h1>directory listing for %s</h1><hr><ul><li><a href=..>..</a></li>", path); - for (;;) { - int len = fread(buf, 1, sizeof buf, f); - if (len <= 0) break; - // TODO directory library - // based on find.c - for (int pos = 0; pos < len; ) { - if (buf[pos] == '\0') break; - const char *end = memchr(buf + pos, 0, len - pos); - if (!end) break; - fprintf(c, "<li><a href=\"%s\">%s</a></li>", buf + pos, buf + pos); - pos += end - (buf + pos) + 1; - } - } - } - fclose(f); -} - -int main(int argc, char **argv) { - const char *path = (argc > 1) ? argv[1] : "/net/listen/0.0.0.0/tcp/80"; - hid_t conn; - for (;;) { - conn = _sys_open(path, strlen(path), OPEN_RW); - if (conn < 0) - errx(1, "open('%s') failed, errno %d", path, -conn); - FILE *f = fdopen(conn, "a+"); - handle(f); - fclose(f); - } -} diff --git a/src/user/app/init/driver/driver.h b/src/user/app/init/driver/driver.h deleted file mode 100644 index 98c18f1..0000000 --- a/src/user/app/init/driver/driver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include <camellia/types.h> - -void initctl_drv(hid_t killswitch); -void ps2_drv(void); -void tmpfs_drv(void); - -void termcook(void); diff --git a/src/user/app/init/driver/initctl.c b/src/user/app/init/driver/initctl.c deleted file mode 100644 index fed71b7..0000000 --- a/src/user/app/init/driver/initctl.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "driver.h" -#include <camellia/syscalls.h> -#include <ctype.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <camellia/compat.h> - -void initctl_drv(hid_t killswitch) { - struct ufs_request res; - char buf[64]; - const size_t buflen = sizeof buf; - while (!c0_fs_wait(buf, buflen, &res)) { - switch (res.op) { - case VFSOP_OPEN: - c0_fs_respond(NULL, res.len == 0 ? 0 : -1, 0); - break; - case VFSOP_WRITE: - /* null terminate */ - if (res.len > buflen - 1) - res.len = buflen - 1; - buf[res.len] = '\0'; - /* cut at first whitespace */ - for (size_t i = 0; buf[i]; i++) { - if (isspace(buf[i])) { - buf[i] = '\0'; - break; - } - } - if (!strcmp(buf, "halt")) { - _sys_write(killswitch, "halt", 4, 0, 0); - } - if (!strcmp(buf, "intr")) { - _sys_write(killswitch, "intr", 4, 0, 0); - } - c0_fs_respond(NULL, res.len, 0); - break; - default: - c0_fs_respond(NULL, -ENOSYS, 0); - break; - } - } - exit(1); -} diff --git a/src/user/app/init/driver/ps2.c b/src/user/app/init/driver/ps2.c deleted file mode 100644 index 5470249..0000000 --- a/src/user/app/init/driver/ps2.c +++ /dev/null @@ -1,129 +0,0 @@ -#include "driver.h" -#include <assert.h> -#include <camellia/compat.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <shared/ring.h> -#include <stdbool.h> -#include <stdlib.h> -#include <thread.h> - - -static const char keymap_lower[] = { - '\0', '\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\r', '\0', 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', '\0', '\'', '`', '\0', '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', '\0', '*', '\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '7', '8', '9', '-', '4', '5', '6', '+', '1', - '2', '3', '0', '.', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', -}; - -static const char keymap_upper[] = { - '\0', '\0', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\r', '\0', 'A', 'S', - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', '\0', '|', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', '<', '>', '?', '\0', '*', '\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '7', '8', '9', '-', '4', '5', '6', '+', '1', - '2', '3', '0', '.', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', -}; - - -static volatile uint8_t backlog_buf[16]; -static volatile ring_t backlog = {(void*)backlog_buf, sizeof backlog_buf, 0, 0}; - -static hid_t fd; - -static bool keys[0x80] = {0}; - -static void parse_scancode(uint8_t s) { - bool ctrl = keys[0x1D]; - bool shift = keys[0x2A] || keys[0x36]; - bool down = !(s & 0x80); - char c; - s &= 0x7f; - keys[s] = down; - - c = shift ? keymap_upper[s] : keymap_lower[s]; - if (ctrl && keymap_upper[s] >= 'A' && keymap_upper[s] <= 'Z') - c = keymap_upper[s] - 'A' + 1; - if (down && c) ring_put1b((void*)&backlog, c); -} - - -/** Is a thread waiting for /kdev/ps2/kb? */ -static volatile bool blocked = false; -/* for use in read_thread */ -static hid_t rt_reqh; -static size_t rt_cap; - -static void read_thread(void *unused) { - char buf[512]; - (void)unused; - - assert(blocked); - while (ring_used((void*)&backlog) == 0) { - /* read raw input until we have something to output */ - int len = _sys_read(fd, buf, sizeof buf, 0); - if (len == 0) break; - for (int i = 0; i < len; i++) - parse_scancode(buf[i]); - } - if (ring_used((void*)&backlog) > 0) { - int ret = ring_get((void*)&backlog, buf, rt_cap); - _sys_fs_respond(rt_reqh, buf, ret, 0); - } else { - _sys_fs_respond(rt_reqh, 0, -EGENERIC, 0); - } - blocked = false; -} - -static void fs_loop(void) { - static char buf[512]; - int ret; - for (;;) { - struct ufs_request res; - hid_t reqh = _sys_fs_wait(buf, sizeof buf, &res); - if (reqh < 0) return; - - switch (res.op) { - case VFSOP_OPEN: - if (res.len == 0) { - _sys_fs_respond(reqh, NULL, 1, 0); - } else { - _sys_fs_respond(reqh, NULL, -ENOENT, 0); - } - break; - - case VFSOP_READ: - if (blocked) { - _sys_fs_respond(reqh, NULL, -EAGAIN, 0); - break; - } else if (ring_used((void*)&backlog) > 0) { - ret = ring_get((void*)&backlog, buf, res.capacity); - _sys_fs_respond(reqh, buf, ret, 0); - } else { - blocked = true; - rt_reqh = reqh; - rt_cap = res.capacity; - thread_create(0, read_thread, 0); - } - break; - - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } -} - -void ps2_drv(void) { - fd = _sys_open("/kdev/ps2/kb", 12, 0); - if (fd < 0) exit(1); - - fs_loop(); - exit(0); -} diff --git a/src/user/app/init/driver/termcook.c b/src/user/app/init/driver/termcook.c deleted file mode 100644 index a76f3a8..0000000 --- a/src/user/app/init/driver/termcook.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "driver.h" -#include <camellia/syscalls.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -enum tstate { - Normal, - Esc, - CSI, -}; - -static void w_output(hid_t output, const char *buf, size_t len) { - size_t pos = 0; - while (pos < len) { - int ret = _sys_write(output, buf + pos, len - pos, pos, 0); - if (ret < 0) break; - pos += ret; - } -} - -static void line_editor(hid_t input, hid_t output) { - char readbuf[16], linebuf[256]; - size_t linepos = 0; - enum tstate state = Normal; - for (;;) { - int readlen = _sys_read(input, readbuf, sizeof readbuf, -1); - if (readlen < 0) return; - for (int i = 0; i < readlen; i++) { - char c = readbuf[i]; - switch (state) { - case Normal: - switch (c) { - case '\b': - case 0x7f: - if (linepos != 0) { - printf("\b \b"); - linepos--; - } - break; - case 3: /* C-c */ - _sys_exit(1); - case 4: /* EOT, C-d */ - if (linepos > 0) { - w_output(output, linebuf, linepos); - linepos = 0; - } else { - _sys_write(output, NULL, 0, 0, 0); /* EOF */ - } - break; - case '\n': - case '\r': - printf("\n"); - if (linepos < sizeof linebuf) - linebuf[linepos++] = '\n'; - w_output(output, linebuf, linepos); - linepos = 0; - break; - case '\e': - state = Esc; - break; - case '\t': - break; - default: - if (linepos < sizeof linebuf) { - linebuf[linepos++] = c; - printf("%c", c); - } - break; - } - break; - case Esc: - if (c == '[') state = CSI; - else state = Normal; - break; - case CSI: - if (0x40 <= c && c <= 0x7E) state = Normal; - break; - } - } - } -} - -void termcook(void) { - hid_t stdin_pipe[2] = {-1, -1}; - if (_sys_pipe(stdin_pipe, 0) < 0) - return; - - if (!fork()) { - /* the caller continues in a child process, - * so it can be killed when the line editor quits */ - _sys_dup(stdin_pipe[0], 0, 0); - close(stdin_pipe[0]); - close(stdin_pipe[1]); - return; - } - if (!fork()) { - close(stdin_pipe[0]); - line_editor(0, stdin_pipe[1]); - exit(0); - } - exit(_sys_await()); -} diff --git a/src/user/app/init/init.c b/src/user/app/init/init.c deleted file mode 100644 index fcebfc7..0000000 --- a/src/user/app/init/init.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "driver/driver.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <camellia/fs/misc.h> - -#define die(fmt, ...) do { fprintf(stderr, "init: " fmt, __VA_ARGS__); exit(1); } while (0) - -static char title[128]; - -void redirect(const char *exe, const char *out, const char *in) { - if (!fork()) { - snprintf(title, sizeof title, "sh >%s", out); - setproctitle(title); - - if (!freopen(out, "a+", stderr)) { - fprintf(stdout, "couldn't open %s\n", out); - exit(1); - } - if (!freopen(out, "a+", stdout)) - die("couldn't open %s\n", out); - if (!freopen(in, "r", stdin)) - die(" couldn't open %s\n", in); - - for (;;) { - if (!fork()) { - const char *argv[] = {exe, NULL}; - termcook(); - execv(exe, (void*)argv); - fprintf(stderr, "init: couldn't start %s\n", exe); - _sys_sleep(5000); - exit(1); - } - _sys_await(); - _sys_intr(); - } - } -} - -int main(void) { - const char *teststr = "I am teststr.\n"; - hid_t killswitch_pipe[2]; - - freopen("/kdev/com1", "a+", stdout); - freopen("/kdev/com1", "a+", stderr); - printf("[init] stage 2, main at %p, teststr at %p\n", &main, teststr); - - MOUNT_AT("/") { - fs_dirinject2((const char*[]){ - "/keyboard/", - "/usr/", - "/bin/", - "/Users/", - "/tmp/", - "/vtty", - "/net/", - "/initctl", - NULL - }); - } - - MOUNT_AT("/keyboard") { - MOUNT_AT("/") { fs_whitelist((const char*[]){"/kdev/ps2/kb", NULL}); } - ps2_drv(); - } - MOUNT_AT("/usr/") { - fs_union((const char*[]){ - "/init/usr/", - NULL - }); - } - MOUNT_AT("/bin/") { - fs_union((const char*[]){ - "/init/bin/amd64/", - "/init/bin/sh/", - "/init/usr/bin/", - "/init/usr/local/bin/", - NULL - }); - } - MOUNT_AT("/Users/") { - MOUNT_AT("/tmp/") { - const char *argv[] = {"/bin/tmpfs", NULL}; - execv(argv[0], (void*)argv); - } - // TODO a simple union isn't enough here - fs_union((const char*[]){ - "/tmp/", - "/init/Users/", - NULL - }); - } - MOUNT_AT("/tmp/") { - const char *allow[] = {"/bin/tmpfs", NULL}; - const char *argv[] = {"/bin/tmpfs", NULL}; - MOUNT_AT("/") { fs_whitelist(allow); } - execv(argv[0], (void*)argv); - } - MOUNT_AT("/vtty") { - const char *allow[] = {"/bin/vterm", "/kdev/video/", "/keyboard", "/init/usr/share/fonts/", NULL}; - const char *argv[] = {"/bin/vterm", NULL}; - MOUNT_AT("/") { fs_whitelist(allow); } - execv(argv[0], (void*)argv); - } - MOUNT_AT("/net/") { - const char *allow[] = {"/bin/netstack", "/kdev/eth", NULL}; - const char *argv[] = {"/bin/netstack", "/kdev/eth", "192.168.0.11", "192.168.0.2", NULL}; - MOUNT_AT("/") { fs_whitelist(allow); } - execv(argv[0], (void*)argv); - } - - if (_sys_pipe(killswitch_pipe, 0) < 0) { - printf("couldn't create the killswitch pipe, quitting...\n"); - return 1; - } - MOUNT_AT("/initctl") { - close(killswitch_pipe[0]); - initctl_drv(killswitch_pipe[1]); - } - close(killswitch_pipe[1]); - - if (!fork()) { - // TODO close on exec - close(killswitch_pipe[0]); - redirect("/bin/shell", "/kdev/com1", "/kdev/com1"); - redirect("/bin/shell", "/vtty", "/keyboard"); - exit(1); - } - - char buf[128]; - for (;;) { - if (_sys_read(killswitch_pipe[0], buf, 128, 0) != 4) { - break; - } - if (memcmp(buf, "intr", 4) == 0) { - _sys_intr(); - } else if (memcmp(buf, "halt", 4) == 0) { - break; - } - } - printf("[init] intr\n"); - _sys_intr(); - _sys_sleep(1000); - printf("[init] filicide\n"); - _sys_filicide(); - printf("[init] goodbye\n"); - return 0; -} diff --git a/src/user/app/iochk/iochk.c b/src/user/app/iochk/iochk.c deleted file mode 100644 index 0850821..0000000 --- a/src/user/app/iochk/iochk.c +++ /dev/null @@ -1,97 +0,0 @@ -#include <assert.h> -#include <camellia.h> -#include <camellia/syscalls.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -static bool verbose = false; - -#define verbosef(...) do { if (verbose) printf(__VA_ARGS__); } while (0) -#define eprintf(fmt, ...) fprintf(stderr, "iochk: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - - -void check(hid_t h) { - const size_t buflen = 4096; - const size_t offsets[] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 16, 32, 64, 128, 256, - 512, 1024, 2048, - }; - char *buflast = malloc(buflen); - char *bufcur = malloc(buflen); - if (!buflast || !bufcur) { - eprintf("out of memory"); - goto end; - } - - long offlast = 0; - long retlast = _sys_read(h, buflast, buflen, offlast); - if (retlast < 0) { - eprintf("error %ld when reading at offset %ld", retlast, offlast); - goto end; - } - - for (size_t i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { - char *tmp; - long offcur = offsets[i]; - long diff = offcur - offlast; - assert(diff >= 0); - if (retlast < diff) break; - - long retcur = _sys_read(h, bufcur, buflen, offcur); - if (retcur < 0) { - eprintf("error %ld when reading at offset %ld", retlast, offcur); - break; - } - if (retcur < retlast + offlast - offcur) { - verbosef("warn: unexpected ret %ld < %ld + %ld - %ld\n", retcur, retlast, offlast, offcur); - } - if (memcmp(buflast + diff, bufcur, retlast - diff)) { - eprintf("inconsistent read from offsets %ld and %ld", offlast, offcur); - } - - offlast = offcur; - retlast = retcur; - tmp = bufcur; - bufcur = buflast; - buflast = tmp; - } - - // TODO check negative offsets - -end: - free(buflast); - free(bufcur); -} - -int main(int argc, char **argv) { - int c; - while ((c = getopt(argc, argv, "v")) != -1) { - switch (c) { - case 'v': - verbose = true; - break; - default: - return 1; - } - } - if (optind >= argc) { - eprintf("no files given"); - return 1; - } - for (; optind < argc; optind++) { - const char *path = argv[optind]; - verbosef("checking %s...\n", path); - hid_t h = camellia_open(path, OPEN_READ); - if (h < 0) { - eprintf("couldn't open %s", path); - continue; - } - check(h); - close(h); - } - return 0; -} diff --git a/src/user/app/iostress/iostress.c b/src/user/app/iostress/iostress.c deleted file mode 100644 index ac555de..0000000 --- a/src/user/app/iostress/iostress.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdio.h> -#include <string.h> -#include <x86intrin.h> - -int main(int argc, char **argv) { - long num_runs = 4; - long num_calls = 512; - long num_bytes = 1; - uint64_t *results; - char *inbuf; - - if (argc > 1) num_runs = strtol(argv[1], NULL, 0); - if (argc > 2) num_calls = strtol(argv[2], NULL, 0); - if (argc > 3) num_bytes = strtol(argv[3], NULL, 0); - if (argc > 4 || num_runs == 0 || num_calls == 0) { - fprintf(stderr, "usage: %s [num_runs] [num_calls] [num_bytes]\n", argv[0]); - return 1; - } - - results = malloc(sizeof(*results) * num_runs); - inbuf = malloc(num_bytes); - memset(inbuf, '.', num_bytes); - - for (long i = 0; i < num_runs; i++) { - uint64_t time = __rdtsc(); - for (long j = 0; j < num_calls; j++) - _sys_write(1, inbuf, num_bytes, -1, 0); - results[i] = __rdtsc() - time; - _sys_write(1, "\n", 1, -1, 0); - } - - uint64_t total = 0; - for (long i = 0; i < num_runs; i++) { - uint64_t scaled = results[i] / 3000; - total += scaled; - fprintf(stderr, "run %ld: %lu\n", i, scaled); - } - fprintf(stderr, "%lu calls, %lu bytes. avg %lu\n", num_calls, num_bytes, total / num_runs); - - return 0; -} diff --git a/src/user/app/logfs/logfs.c b/src/user/app/logfs/logfs.c deleted file mode 100644 index a50d530..0000000 --- a/src/user/app/logfs/logfs.c +++ /dev/null @@ -1,34 +0,0 @@ -#include <camellia.h> -#include <camellia/syscalls.h> -#include <err.h> -#include <stdio.h> -#include <stdlib.h> -#include <camellia/fs/misc.h> - -_Noreturn void fs(void) { - const size_t buflen = 1024; - char *buf = malloc(buflen); - if (!buf) err(1, "malloc"); - for (;;) { - struct ufs_request req; - hid_t reqh = ufs_wait(buf, buflen, &req); - if (reqh < 0) errx(1, "ufs_wait error"); - - switch (req.op) { - case VFSOP_OPEN: - printf("[logfs] open(\"%s\", 0x%x)\n", buf, req.flags); - forward_open(reqh, buf, req.len, req.flags); - break; - default: - /* Unsupported vfs operation. - * Currently if you never create your own file descriptors you won't receive - * anything but VFSOP_OPEN, but it's idiomatic to handle this anyways. */ - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } -} - -int main(void) { - fs(); -} diff --git a/src/user/app/login/login.c b/src/user/app/login/login.c deleted file mode 100644 index 0f9e8b7..0000000 --- a/src/user/app/login/login.c +++ /dev/null @@ -1,89 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <camellia/fs/misc.h> - -static const char *shell = "/bin/shell"; - -static void cutspace(char *s) { - for (; *s; s++) { - if (isspace(*s)) { - *s = '\0'; - break; - } - } -} - -static bool segcmp(const char *path, int idx, const char *s2) { - if (idx < 0) return false; - while (idx > 0) { - if (*path == '\0') return false; - if (*path == '/') idx--; - path++; - } - /* path is at the start of the selected segment */ - while (*s2 && *path++ == *s2++); - return (*path == '\0' || *path == '/') && *s2 == '\0'; -} - -static void drv(const char *user) { - char *buf = malloc(PATH_MAX); - for (;;) { - struct ufs_request req; - hid_t reqh = ufs_wait(buf, PATH_MAX, &req); - if (reqh < 0) break; - switch (req.op) { - case VFSOP_OPEN: - if (segcmp(buf, 1, "Users") && segcmp(buf, 2, user)) { // /Users/$user/** - forward_open(reqh, buf, req.len, req.flags); - } else if (segcmp(buf, 1, "Users") && segcmp(buf, 3, "private")) { // /Users/*/private/** - _sys_fs_respond(reqh, NULL, -EACCES, 0); - } else if (!OPEN_WRITEABLE(req.flags)) { - forward_open(reqh, buf, req.len, req.flags); - } else { - _sys_fs_respond(reqh, NULL, -EACCES, 0); - } - break; - - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - free(buf); -} - -static void trylogin(const char *user) { - if (strcmp(user, "root") != 0) { - char buf[128]; - snprintf(buf, sizeof buf, "/Users/%s/", user); - if (chdir(buf) < 0) { - printf("no such user: %s\n", user); - return; - } - MOUNT_AT("/") { drv(user); } - } - - execv(shell, NULL); - fprintf(stderr, "login: couldn't launch %s\n", shell); - exit(1); -} - -int main(void) { - char user[64]; - printf("\nCamellia\n"); - for (;;) { - printf("login: "); - fgets(user, sizeof user, stdin); - if (ferror(stdin)) return -1; - - cutspace(user); - if (user[0]) trylogin(user); - } -} diff --git a/src/user/app/netdog/nd.c b/src/user/app/netdog/nd.c deleted file mode 100644 index af5c264..0000000 --- a/src/user/app/netdog/nd.c +++ /dev/null @@ -1,48 +0,0 @@ -#include <camellia.h> -#include <camellia/syscalls.h> -#include <stdio.h> -#include <string.h> -#include <thread.h> - -#define eprintf(fmt, ...) fprintf(stderr, "netdog: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - -hid_t conn; - -void send_stdin(void *arg) { (void)arg; - static char buf[4096]; - for (;;) { - // TODO define STDIN_FILENO - long ret = _sys_read(0, buf, sizeof buf, -1); - if (ret <= 0) return; /* instead of sending an empty packet, quit. */ - ret = _sys_write(conn, buf, ret, -1, 0); - if (ret < 0) return; - } -} - -void recv_stdout(void *arg) { (void)arg; - static char buf[4096]; - for (;;) { - long ret = _sys_read(conn, buf, sizeof buf, -1); - if (ret < 0) return; - ret = _sys_write(1, buf, ret, -1, 0); - if (ret < 0) return; - } -} - -int main(int argc, char **argv) { - if (argc < 2) { - eprintf("no argument"); - return 1; - } - - conn = camellia_open(argv[1], OPEN_RW); - if (conn < 0) { - eprintf("couldn't open '%s', err %u", argv[1], -conn); - return -conn; - } - - thread_create(0, send_stdin, NULL); - thread_create(0, recv_stdout, NULL); - _sys_await(); - return 0; -} diff --git a/src/user/app/netstack/arp.c b/src/user/app/netstack/arp.c deleted file mode 100644 index 3a1c8da..0000000 --- a/src/user/app/netstack/arp.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "proto.h" -#include "util.h" -#include <assert.h> -#include <camellia/syscalls.h> -#include <string.h> - -enum { - HdrType = 0, - HdrTypeEther = 1, - ProtoType = 2, - HdrALen = 4, - ProtoALen = 5, - Operation = 6, - OpReq = 1, - OpReply = 2, -}; - -struct arpc { - struct arpc *next; - uint32_t ip; - mac_t mac; -}; -static struct arpc *arpcache; -static void arpcache_put(uint32_t ip, mac_t mac); - -void arp_parse(const uint8_t *buf, size_t len) { - if (len < Operation + 2) return; - uint16_t htype = nget16(buf + HdrType); - uint16_t ptype = nget16(buf + ProtoType); - uint16_t op = nget16(buf + Operation); - - if (!(htype == HdrTypeEther && ptype == ET_IPv4)) return; - enum { /* only valid for this combination of header + proto */ - SrcMAC = 8, - SrcIP = 14, - DstMAC = 18, - DstIP = 24, - }; - if (len < DstIP + 4) return; - arpcache_put(nget32(buf + SrcIP), *(mac_t*)buf + SrcMAC); - - if (op == OpReq) { - uint32_t daddr = nget32(buf + DstIP); - if (daddr == state.ip) { - uint8_t *pkt = ether_start(30, (struct ethernet){ - .dst = (void*)(buf + SrcMAC), - .type = ET_ARP, - }); - nput16(pkt + HdrType, HdrTypeEther); - nput16(pkt + ProtoType, ET_IPv4); - pkt[HdrALen] = 6; - pkt[ProtoALen] = 4; - nput16(pkt + Operation, OpReply); - memcpy(pkt + SrcMAC, state.mac, 6); - nput32(pkt + SrcIP, state.ip); - memcpy(pkt + DstMAC, buf + SrcMAC, 10); /* sender's MAC and IP */ - ether_finish(pkt); - } - } -} - -void arp_request(uint32_t ip) { - enum { - SrcMAC = 8, - SrcIP = 14, - DstMAC = 18, - DstIP = 24, - }; - uint8_t *pkt = ether_start(28, (struct ethernet){ - .src = &state.mac, - .dst = &MAC_BROADCAST, - .type = ET_ARP, - }); - nput16(pkt + HdrType, HdrTypeEther); - nput16(pkt + ProtoType, ET_IPv4); - pkt[HdrALen] = 6; - pkt[ProtoALen] = 4; - nput16(pkt + Operation, OpReq); - memcpy(pkt + SrcMAC, state.mac, 6); - nput32(pkt + SrcIP, state.ip); - memcpy(pkt + DstMAC, &MAC_BROADCAST, 6); - nput32(pkt + DstIP, ip); - ether_finish(pkt); -} - -static void arpcache_put(uint32_t ip, mac_t mac) { - for (struct arpc *iter = arpcache; iter; iter = iter->next) { - if (memcmp(iter->mac, mac, 6) == 0) { - if (iter->ip == ip) return; /* cache entry correct */ - else break; /* cache entry needs updating */ - } - } - struct arpc *e = malloc(sizeof *e); - e->next = arpcache; - e->ip = ip; - memcpy(e->mac, mac, 6); - arpcache = e; -} - -int arpcache_get(uint32_t ip, mac_t *mac) { - for (struct arpc *iter = arpcache; iter; iter = iter->next) { - if (iter->ip == ip) { - if (mac) memcpy(mac, iter->mac, 6); - return 0; - } - } - return -1; -} - -void arp_fsread(hid_t h, long offset) { - const char *fmt = "%08x\t%02x:%02x:%02x:%02x:%02x:%02x\n"; - long linelen = snprintf(NULL, 0, fmt, 0, 1, 2, 3, 4, 5, 6) + 1; - char buf[28]; - assert(linelen <= (long)sizeof(buf)); - if (offset < 0) goto err; - - struct arpc *cur = arpcache; - if (!cur) goto err; - for (; linelen <= offset; offset -= linelen) { - cur = cur->next; - if (!cur) goto err; - } - assert(0 <= offset && offset < linelen); - - snprintf(buf, sizeof buf, fmt, cur->ip, - cur->mac[0], - cur->mac[1], - cur->mac[2], - cur->mac[3], - cur->mac[4], - cur->mac[5]); - _sys_fs_respond(h, buf + offset, linelen - offset, 0); - return; -err: - _sys_fs_respond(h, NULL, -1, 0); -} - -long arp_fswrite(const char *buf, long len, long offset) { - if (offset != -1) return -1; - uint32_t ip; - char tmp[16]; - size_t iplen = len < 15 ? len : 15; - memcpy(tmp, buf, iplen); - tmp[iplen] = '\0'; - if (ip_parse(tmp, &ip) < 0) { - return -1; - } else { - arp_request(ip); - return len; - } -} diff --git a/src/user/app/netstack/ether.c b/src/user/app/netstack/ether.c deleted file mode 100644 index 52abac2..0000000 --- a/src/user/app/netstack/ether.c +++ /dev/null @@ -1,59 +0,0 @@ -#include <camellia/syscalls.h> -#include "proto.h" -#include "util.h" - -enum { - DstMAC = 0, - SrcMAC = 6, - EtherType = 12, - Payload = 14, -}; -struct ethq *ether_queue; - -void ether_parse(const uint8_t *buf, size_t len) { - struct ethernet ether = (struct ethernet){ - .src = (void*)(buf + SrcMAC), - .dst = (void*)(buf + DstMAC), - .type = nget16(buf + EtherType), - }; - - for (struct ethq **iter = ðer_queue; iter && *iter; ) { - struct ethq *qe = *iter; - _sys_fs_respond(qe->h, buf, len, 0); - /* remove entry */ - /* yes, doing it this way here doesn't make sense. i'm preparing for filtering */ - *iter = qe->next; - free(qe); - } - - switch (ether.type) { - case ET_IPv4: - ipv4_parse(buf + Payload, len - Payload, ether); - break; - case ET_ARP: - arp_parse(buf + Payload, len - Payload); - break; - } -} - -static const size_t fhoff = sizeof(size_t); -uint8_t *ether_start(size_t len, struct ethernet ether) { - if (len < 60 - Payload) len = 60 - Payload; - - if (!ether.dst) eprintf("NULL ether.dst!"); // TODO arp? i guess? - if (!ether.src) ether.src = &state.mac; - - uint8_t *buf = malloc(fhoff + Payload + len); - memset(buf, 0, fhoff + Payload + len); - *(size_t*)buf = len + Payload; - memcpy(buf + fhoff + DstMAC, ether.dst, 6); - memcpy(buf + fhoff + SrcMAC, ether.src, 6); - nput16(buf + fhoff + EtherType, ether.type); - return buf + fhoff + Payload; -} -void ether_finish(uint8_t *pkt) { - uint8_t *buf = pkt - Payload - fhoff; - size_t len = *(size_t*)buf; - _sys_write(state.raw_h, buf + fhoff, len, 0, 0); - free(buf); -} diff --git a/src/user/app/netstack/fs.c b/src/user/app/netstack/fs.c deleted file mode 100644 index 6d51c35..0000000 --- a/src/user/app/netstack/fs.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * path format: - * /net/raw - * raw ethernet frames (read-write) - * /net/arp - * ARP cache (currently read-only) - * /net/connect/0.0.0.0/1.2.3.4/udp/53 - * connect from 0.0.0.0 (any ip) to 1.2.3.4 on udp port 53 - * /net/listen/0.0.0.0/{tcp,udp}/53 - * waits for a connection to any ip on udp port 53 - * open() returns once a connection to ip 0.0.0.0 on udp port 53 is received - */ -#include "proto.h" -#include "util.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -enum handle_type { - H_ETHER, - H_TCP, - H_UDP, - H_ARP, -}; - -struct strqueue { - struct strqueue *next; - size_t len; - char buf[]; -}; - -struct handle { - enum handle_type type; - struct { - struct udp_conn *c; - struct strqueue *rx, *rxlast; - } udp; - struct { - struct tcp_conn *c; - size_t readcap; - } tcp; - bool dead; - hid_t reqh; -}; - - -static void tcp_listen_callback(struct tcp_conn *c, void *arg) { - struct handle *h = arg; - h->tcp.c = c; - _sys_fs_respond(h->reqh, h, 0, 0); - h->reqh = -1; -} - -/* also called from recv_enqueue. yes, it's a mess */ -static void tcp_recv_callback(void *arg) { - struct handle *h = arg; - char buf[1024]; - if (h->reqh >= 0) { - if (h->tcp.readcap > sizeof buf) - h->tcp.readcap = sizeof buf; - size_t len = tcpc_tryread(h->tcp.c, buf, h->tcp.readcap); - if (len > 0) { - _sys_fs_respond(h->reqh, buf, len, 0); - h->reqh = -1; - } - } -} - -static void tcp_close_callback(void *arg) { - struct handle *h = arg; - h->dead = true; - if (h->reqh >= 0) { - _sys_fs_respond(h->reqh, NULL, -ECONNRESET, 0); - h->reqh = -1; - return; - } -} - -static void udp_listen_callback(struct udp_conn *c, void *arg) { - struct handle *h = arg; - h->udp.c = c; - _sys_fs_respond(h->reqh, h, 0, 0); - h->reqh = -1; -} - -static void udp_recv_callback(const void *buf, size_t len, void *arg) { - struct handle *h = arg; - if (h->reqh >= 0) { - _sys_fs_respond(h->reqh, buf, len, 0); - h->reqh = -1; - return; - } - struct strqueue *sq = malloc(sizeof(*sq) + len); - sq->next = NULL; - sq->len = len; - memcpy(sq->buf, buf, len); - if (h->udp.rx) { - h->udp.rxlast->next = sq; - h->udp.rxlast = sq; - } else { - h->udp.rx = sq; - h->udp.rxlast = sq; - } -} - -static void recv_enqueue(struct handle *h, hid_t reqh, size_t readcap) { - if (h->reqh > 0) { - // TODO queue - _sys_fs_respond(reqh, NULL, -1, 0); - return; - } - if (h->type == H_UDP && h->udp.rx) { - _sys_fs_respond(reqh, h->udp.rx->buf, h->udp.rx->len, 0); - h->udp.rx = h->udp.rx->next; - free(h->udp.rx); - return; - } - h->reqh = reqh; - if (h->type == H_TCP) { - h->tcp.readcap = readcap; - tcp_recv_callback(h); - } -} - -static void fs_open(hid_t reqh, char *path, int flags) { -#define respond(buf, val) do{ _sys_fs_respond(reqh, buf, val, 0); return; }while(0) - struct handle *h; - if (*path != '/') respond(NULL, -1); - path++; - - if (strcmp(path, "raw") == 0) { - h = malloc(sizeof *h); - memset(h, 0, sizeof *h); - h->type = H_ETHER; - respond(h, 0); - } else if (strcmp(path, "arp") == 0) { - h = malloc(sizeof *h); - memset(h, 0, sizeof *h); - h->type = H_ARP; - respond(h, 0); - } - - /* everything below ends up sending packets */ - if (!OPEN_WRITEABLE(flags)) - respond(NULL, -EACCES); - - char *save; - const char *verb, *proto, *port_s; - uint32_t srcip, dstip; - - verb = strtok_r(path, "/", &save); - if (!verb) respond(NULL, -1); - - if (ip_parse(strtok_r(NULL, "/", &save), &srcip) < 0) - respond(NULL, -1); - if (srcip != 0) { - eprintf("unimplemented"); - respond(NULL, -1); - } - - if (strcmp(verb, "listen") == 0) { - proto = strtok_r(NULL, "/", &save); - if (!proto) respond(NULL, -1); - if (strcmp(proto, "udp") == 0) { - port_s = strtok_r(NULL, "/", &save); - if (port_s) { - uint16_t port = strtol(port_s, NULL, 0); - h = malloc(sizeof *h); - memset(h, 0, sizeof *h); - h->type = H_UDP; - h->reqh = reqh; - udp_listen(port, udp_listen_callback, udp_recv_callback, h); - return; - } - } - if (strcmp(proto, "tcp") == 0) { - port_s = strtok_r(NULL, "/", &save); - if (port_s) { - uint16_t port = strtol(port_s, NULL, 0); - h = malloc(sizeof *h); - memset(h, 0, sizeof *h); - h->type = H_TCP; - h->reqh = reqh; - tcp_listen(port, tcp_listen_callback, tcp_recv_callback, tcp_close_callback, h); - return; - } - } - } else if (strcmp(verb, "connect") == 0) { - if (ip_parse(strtok_r(NULL, "/", &save), &dstip) < 0) - respond(NULL, -1); - proto = strtok_r(NULL, "/", &save); - if (!proto) respond(NULL, -1); - if (strcmp(proto, "tcp") == 0) { - port_s = strtok_r(NULL, "/", &save); - if (port_s) { - uint16_t port = strtol(port_s, NULL, 0); - h = malloc(sizeof *h); - memset(h, 0, sizeof *h); - h->type = H_TCP; - h->tcp.c = tcpc_new((struct tcp){ - .dst = port, - .ip.dst = dstip, - }, tcp_recv_callback, tcp_close_callback, h); - if (h->tcp.c) { - respond(h, 0); - } else { - free(h); - respond(NULL, -1); - } - } - } - if (strcmp(proto, "udp") == 0) { - port_s = strtok_r(NULL, "/", &save); - if (port_s) { - uint16_t port = strtol(port_s, NULL, 0); - h = malloc(sizeof *h); - memset(h, 0, sizeof *h); - h->type = H_UDP; - h->udp.c = udpc_new((struct udp){ - .dst = port, - .ip.dst = dstip, - }, udp_recv_callback, h); - if (h->udp.c) { - respond(h, 0); - } else { - free(h); - respond(NULL, -1); - } - } - } - } - respond(NULL, -1); -#undef respond -} - -void fs_thread(void *arg) { (void)arg; - const size_t buflen = 4096; - char *buf = malloc(buflen); - for (;;) { - struct ufs_request res; - hid_t reqh = _sys_fs_wait(buf, buflen, &res); - if (reqh < 0) break; - struct handle *h = res.id; - long ret; - switch (res.op) { - case VFSOP_OPEN: - if (res.len < buflen) { - buf[res.len] = '\0'; - fs_open(reqh, buf, res.flags); - } else { - _sys_fs_respond(reqh, NULL, -1, 0); - } - break; - case VFSOP_READ: - if (h->dead) { - _sys_fs_respond(reqh, NULL, -ECONNRESET, 0); - break; - } - switch (h->type) { - case H_ETHER: { - struct ethq *qe; - qe = malloc(sizeof *qe); - qe->h = reqh; - qe->next = ether_queue; - ether_queue = qe; - break;} - case H_TCP: - case H_UDP: - recv_enqueue(h, reqh, res.capacity); - break; - case H_ARP: - arp_fsread(reqh, res.offset); - break; - default: - _sys_fs_respond(reqh, NULL, -1, 0); - } - break; - case VFSOP_WRITE: - if (h->dead) { - _sys_fs_respond(reqh, NULL, -ECONNRESET, 0); - break; - } - switch (h->type) { - case H_ETHER: - ret = _sys_write(state.raw_h, buf, res.len, 0, 0); - _sys_fs_respond(reqh, NULL, ret, 0); - break; - case H_TCP: - tcpc_send(h->tcp.c, buf, res.len); - _sys_fs_respond(reqh, NULL, res.len, 0); - break; - case H_UDP: - udpc_send(h->udp.c, buf, res.len); - _sys_fs_respond(reqh, NULL, res.len, 0); - break; - case H_ARP: - _sys_fs_respond(reqh, NULL, arp_fswrite(buf, res.len, res.offset), 0); - break; - default: - _sys_fs_respond(reqh, NULL, -1, 0); - } - break; - case VFSOP_CLOSE: - // TODO remove entries in queue - // TODO why does close even have _sys_fs_respond? - if (h->type == H_TCP) tcpc_close(h->tcp.c); - if (h->type == H_UDP) udpc_close(h->udp.c); - free(h); - _sys_fs_respond(reqh, NULL, -1, 0); - break; - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - free(buf); -} diff --git a/src/user/app/netstack/icmp.c b/src/user/app/netstack/icmp.c deleted file mode 100644 index 0c6a502..0000000 --- a/src/user/app/netstack/icmp.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "proto.h" -#include "util.h" - -enum { - Type = 0, - Code = 1, - Checksum = 2, - Payload = 4, -}; - -void icmp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { - if (len < Payload) return; - uint8_t type = buf[Type]; - if (type == 8 && ip.dst == state.ip) { - /* echo reply */ - icmp_send(buf + Payload, len - Payload, (struct icmp){ - .type = 0, - .ip.dst = ip.src, - .ip.e.dst = ip.e.src, - }); - } -} - -void icmp_send(const void *payload, size_t len, struct icmp i) { - i.ip.proto = 1; - uint8_t *pkt = malloc(Payload + len); - pkt[Type] = i.type; - pkt[Code] = i.code; - memcpy(pkt + Payload, payload, len); - nput16(pkt + Checksum, 0); - nput16(pkt + Checksum, ip_checksum(pkt, Payload + len)); - ipv4_send(pkt, Payload + len, i.ip); - free(pkt); -} diff --git a/src/user/app/netstack/ipv4.c b/src/user/app/netstack/ipv4.c deleted file mode 100644 index 1336dc1..0000000 --- a/src/user/app/netstack/ipv4.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "proto.h" -#include "util.h" -#include <assert.h> -#include <stdlib.h> - -enum { - Version = 0, - HdrLen = 0, - TotalLen = 2, - Id = 4, - FragInfo = 6, - EvilBit = 0x8000, - DontFrag = 0x4000, - MoreFrags = 0x2000, - FragOff = 0x1FFF, - TTL = 8, - Proto = 9, - Checksum = 10, - SrcIP = 12, - DstIP = 16, -}; -static void ipv4_dispatch(const uint8_t *buf, size_t len, struct ipv4 ip); - -struct fragment { - struct fragment *next; /* sorted */ - size_t len, offset; - bool last; - uint8_t buf[]; -}; -struct fragmented { - /* src, dst, proto, id come from the first arrived packet - * and are used to tell fragmenteds apart. - * the rest comes from the sequentially first packet. - * ip.h.header points to a malloc'd buffer*/ - struct ipv4 h; - - struct fragment *first; - struct fragmented *next, **prev; /* *(inc->prev) == inc */ - // TODO timer -}; -struct fragmented *fragmenteds; -static struct fragmented *fragmented_find(struct ipv4 fraghdr); -static void fragmented_tryinsert(const uint8_t *payload, size_t plen, struct ipv4 ip); -static void fragmented_tryfinish(struct fragmented *inc); -static void fragmented_free(struct fragmented *inc); - -static struct fragmented *fragmented_find(struct ipv4 fraghdr) { - struct fragmented *inc; - for (inc = fragmenteds; inc; inc = inc->next) { - if (inc->h.src == fraghdr.src && - inc->h.dst == fraghdr.dst && - inc->h.proto == fraghdr.proto && - inc->h.id == fraghdr.id) - { - return inc; - } - } - inc = malloc(sizeof *inc); - memset(inc, 0, sizeof *inc); - inc->h.src = fraghdr.src; - inc->h.dst = fraghdr.dst; - inc->h.proto = fraghdr.proto; - inc->h.id = fraghdr.id; - - inc->next = fragmenteds; - if (inc->next) inc->next->prev = &inc->next; - inc->prev = &fragmenteds; - *inc->prev = inc; - return inc; -} - -static void fragmented_tryinsert(const uint8_t *payload, size_t plen, struct ipv4 ip) { - struct fragmented *inc = fragmented_find(ip); - size_t off = (ip.fraginfo & FragOff) * 8; - bool last = !(ip.fraginfo & MoreFrags); - // eprintf("fragmented packet, %u + %u, part of 0x%x", off, plen, inc); - - /* find the first fragment at a bigger offset, and insert before it */ - struct fragment **insert = &inc->first; - for (; *insert; insert = &(*insert)->next) { - if ((*insert)->offset > off) break; - if ((*insert)->offset == off) return; /* duplicate packet */ - } - /* later on: frag->next = *insert; - * if we're the last fragment, frag->next must == NULL */ - if (last && *insert != NULL) return; - - struct fragment *frag = malloc(sizeof(struct fragment) + plen); - frag->next = *insert; - *insert = frag; - frag->len = plen; - frag->offset = off; - frag->last = last; - memcpy(frag->buf, payload, plen); - - if (off == 0) { /* save header */ - assert(!inc->h.header); - void *headercpy = malloc(ip.hlen); - memcpy(headercpy, ip.header, ip.hlen); - inc->h = ip; - inc->h.header = headercpy; - } - - fragmented_tryfinish(inc); -} - -static void fragmented_tryfinish(struct fragmented *inc) { - if (inc->first->offset != 0) return; - for (struct fragment *iter = inc->first; iter; iter = iter->next) { - size_t iterend = iter->offset + iter->len; - struct fragment *next = iter->next; - if (next) { - if (iterend < next->offset) return; /* incomplete */ - if (iterend > next->offset) { - fragmented_free(inc); - return; - } - } else if (iter->last) { - void *buf = malloc(iterend); - for (struct fragment *iter = inc->first; iter; iter = iter->next) { - assert(iter->offset + iter->len <= iterend); - memcpy(buf + iter->offset, iter->buf, iter->len); - } - ipv4_dispatch(buf, iterend, inc->h); - free(buf); - fragmented_free(inc); - } - } -} - -static void fragmented_free(struct fragmented *inc) { - if (inc->next) { - inc->next->prev = inc->prev; - *inc->next->prev = inc->next; - } else { - *inc->prev = NULL; - } - - for (struct fragment *next, *iter = inc->first; iter; iter = next) { - next = iter->next; - free(iter); - } - free((void*)inc->h.header); - free(inc); -} - - -static void ipv4_dispatch(const uint8_t *buf, size_t len, struct ipv4 ip) { - switch (ip.proto) { - case 0x01: icmp_parse(buf, len, ip); break; - case 0x06: tcp_parse(buf, len, ip); break; - case 0x11: udp_parse(buf, len, ip); break; - } -} - -void ipv4_parse(const uint8_t *buf, size_t len, struct ethernet ether) { - uint8_t version, headerlen; - uint16_t totallen; - - version = buf[Version] >> 4; - if (version != 4) return; - headerlen = (buf[HdrLen] & 0xf) * 4; - totallen = nget16(buf + TotalLen); - if (totallen < headerlen) return; - - /* ignores checksum. TODO? */ - - struct ipv4 ip = (struct ipv4){ - .e = ether, - .src = nget32(buf + SrcIP), - .dst = nget32(buf + DstIP), - .id = nget16(buf + Id), - .fraginfo = nget16(buf + FragInfo), - .proto = buf[Proto], - .header = buf, - .hlen = headerlen, - }; - - if (ip.fraginfo & ~(EvilBit | DontFrag)) { - fragmented_tryinsert(buf + headerlen, totallen - headerlen, ip); - } else { - if (totallen > len) return; - ipv4_dispatch(buf + headerlen, totallen - headerlen, ip); - } -} - -static uint16_t next_id = 0; -void ipv4_send(const void *payload, size_t len, struct ipv4 ip) { - const size_t mtu = 1500; - const size_t hdrlen = 20; - - ip.e.type = ET_IPv4; - if (!ip.src) ip.src = state.ip; - if (!ip.e.dst && ip.dst == 0xFFFFFFFF) - ip.e.dst = &MAC_BROADCAST; - - uint16_t id = next_id++; - for (size_t off = 0, fraglen = mtu - hdrlen; off < len; off += fraglen) { - if (fraglen > len - off) - fraglen = len - off; - bool last = off + fraglen >= len; - uint8_t *pkt = ether_start(hdrlen + fraglen, ip.e); - pkt[Version] = 0x40; - pkt[HdrLen] |= hdrlen / 4; - nput16(pkt + TotalLen, hdrlen + fraglen); - nput16(pkt + Id, id); - nput16(pkt + FragInfo, (off >> 3) | (last ? 0 : MoreFrags)); - pkt[TTL] = 0xFF; - pkt[Proto] = ip.proto; - nput32(pkt + SrcIP, ip.src); - nput32(pkt + DstIP, ip.dst); - nput16(pkt + Checksum, ip_checksum(pkt, hdrlen)); - memcpy(pkt + hdrlen, payload + off, fraglen); - ether_finish(pkt); - } -} diff --git a/src/user/app/netstack/netstack.c b/src/user/app/netstack/netstack.c deleted file mode 100644 index 2636429..0000000 --- a/src/user/app/netstack/netstack.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "proto.h" -#include "util.h" -#include <camellia.h> -#include <camellia/syscalls.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <thread.h> - -struct net_state state = { - // TODO dynamically get mac - .mac = {0x52, 0x54, 0x00, 0xCA, 0x77, 0x1A}, -}; - -void network_thread(void *arg) { (void)arg; - const size_t buflen = 4096; - char *buf = malloc(buflen); - for (;;) { - long ret = _sys_read(state.raw_h, buf, buflen, -1); - if (ret < 0) break; - ether_parse((void*)buf, ret); - } - free(buf); -} - -void fs_thread(void *arg); - -int main(int argc, char **argv) { - if (argc < 4) { - eprintf("usage: netstack iface ip gateway"); - return 1; - } - state.raw_h = camellia_open(argv[1], OPEN_RW); - if (state.raw_h < 0) { - eprintf("couldn't open %s", argv[1]); - return 1; - } - if (ip_parse(argv[2], &state.ip) < 0) { - eprintf("invalid ip"); - return -1; - } - if (ip_parse(argv[3], &state.gateway) < 0) { - eprintf("invalid gateway"); - return -1; - } - setproctitle(argv[2]); - arp_request(state.gateway); - thread_create(0, network_thread, NULL); - thread_create(0, fs_thread, NULL); - _sys_await(); - return 0; -} diff --git a/src/user/app/netstack/proto.h b/src/user/app/netstack/proto.h deleted file mode 100644 index 8ea11ac..0000000 --- a/src/user/app/netstack/proto.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -#include <camellia/types.h> -#include <stdbool.h> -#include <stdint.h> - -typedef uint8_t mac_t[6]; -static const mac_t MAC_BROADCAST = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - -extern struct net_state { - mac_t mac; - uint32_t ip, gateway; - - hid_t raw_h; -} state; - -enum { /* ethertype */ - ET_IPv4 = 0x800, - ET_ARP = 0x806, -}; - -struct ethernet { - const mac_t *src, *dst; - uint16_t type; -}; - -struct ipv4 { - struct ethernet e; - uint32_t src, dst; - uint16_t id, fraginfo; - uint8_t proto; - const uint8_t *header; size_t hlen; -}; - -struct tcp { - struct ipv4 ip; - uint16_t src, dst; -}; - -struct udp { - struct ipv4 ip; - uint16_t src, dst; -}; - -struct icmp { - struct ipv4 ip; - uint8_t type, code; -}; - - -/* NOT THREADSAFE, YET USED FROM CONCURRENT THREADS - * will break if i implement a scheduler*/ -struct ethq { - struct ethq *next; - hid_t h; -}; -extern struct ethq *ether_queue; - -void arp_parse(const uint8_t *buf, size_t len); -void arp_request(uint32_t ip); -/* 0 on success, -1 on failure */ -int arpcache_get(uint32_t ip, mac_t *mac); -void arp_fsread(hid_t h, long offset); -long arp_fswrite(const char *buf, long len, long offset); - -void icmp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); -void icmp_send(const void *payload, size_t len, struct icmp i); - -void ipv4_parse(const uint8_t *buf, size_t len, struct ethernet ether); -void ipv4_send(const void *payload, size_t len, struct ipv4 ip); - -void ether_parse(const uint8_t *buf, size_t len); -uint8_t *ether_start(size_t len, struct ethernet ether); -void ether_finish(uint8_t *pkt); - -struct udp_conn; -void udp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); -/* calls callback once, after a client connects. */ -void udp_listen( - uint16_t port, - void (*on_conn)(struct udp_conn *, void *carg), - void (*on_recv)(const void *, size_t, void *carg), - void *carg); -struct udp_conn *udpc_new( - struct udp u, - void (*on_recv)(const void *, size_t, void *carg), - void *carg); -// TODO udp_onclosed -void udpc_send(struct udp_conn *, const void *buf, size_t len); -/* frees */ -void udpc_close(struct udp_conn *); - -struct tcp_conn; -void tcp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); -void tcp_listen( - uint16_t port, - void (*on_conn)(struct tcp_conn *, void *carg), - void (*on_recv)(void *carg), - void (*on_close)(void *carg), - void *carg); -struct tcp_conn *tcpc_new( - struct tcp t, - void (*on_recv)(void *carg), - void (*on_close)(void *carg), - void *carg); -size_t tcpc_tryread(struct tcp_conn *, void *buf, size_t len); -void tcpc_send(struct tcp_conn *, const void *buf, size_t len); -void tcpc_close(struct tcp_conn *); diff --git a/src/user/app/netstack/tcp.c b/src/user/app/netstack/tcp.c deleted file mode 100644 index d1adeab..0000000 --- a/src/user/app/netstack/tcp.c +++ /dev/null @@ -1,268 +0,0 @@ -/* Welcome to spaghetti land. - * This is anything but production quality. It's throwaway code, meant - * only to see how networking could fit into the architecture of the - * system. */ -#include "proto.h" -#include "util.h" -#include <assert.h> -#include <shared/ring.h> - -enum { - SrcPort = 0, - DstPort = 2, - Seq = 4, - AckNum = 8, /* last processed seq + 1 */ - Flags = 12, - FlagSize = 0xF000, /* size of header / 4 bytes */ - /* 3 bits: 0, still unused - * 3 bits: modern stuff not in the original RFC */ - FlagURG = 0x0020, /* urgent */ - FlagACK = 0x0010, - FlagPSH = 0x0008, /* force buffer flush */ - FlagRST = 0x0004, /* reset connection */ - FlagSYN = 0x0002, /* first packet; sync sequence numbers */ - FlagFIN = 0x0001, /* last packet */ - FlagAll = 0x003F, - WinSize = 14, /* amount of data we're willing to receive */ - Checksum = 16, - Urgent = 18, - MinHdr = 20, -}; - -enum tcp_state { - LISTEN, - SYN_SENT, - SYN_RCVD, - ESTABILISHED, - LAST_ACK, - CLOSED, -}; - -struct tcp_conn { - uint32_t lip, rip; - mac_t rmac; - uint16_t lport, rport; - struct tcp_conn *next, **link; - - enum tcp_state state; - uint32_t lack, rack; - uint32_t lseq; - bool uclosed; /* did the user close? */ - - void (*on_conn)(struct tcp_conn *, void *carg); - void (*on_recv)(void *carg); - void (*on_close)(void *carg); - void *carg; - - ring_t rx; -}; -static struct tcp_conn *conns; -static void conns_append(struct tcp_conn *c) { - c->next = conns; - if (c->next) - c->next->link = &c->next; - c->link = &conns; - *c->link = c; -} -static void tcpc_sendraw(struct tcp_conn *c, uint16_t flags, const void *buf, size_t len) { - uint8_t *pkt = malloc(MinHdr + len); - memset(pkt, 0, MinHdr); - - nput16(pkt + SrcPort, c->lport); - nput16(pkt + DstPort, c->rport); - nput32(pkt + Seq, c->lseq); - c->lseq += len; - nput32(pkt + AckNum, c->lack); - flags |= (MinHdr / 4) << 12; - nput16(pkt + Flags, flags); - nput16(pkt + WinSize, ring_avail(&c->rx)); - memcpy(pkt + MinHdr, buf, len); - nput16(pkt + Checksum, ip_checksumphdr(pkt, MinHdr + len, c->lip, c->rip, 6)); - - ipv4_send(pkt, MinHdr + len, (struct ipv4){ - .proto = 6, - .src = c->lip, - .dst = c->rip, - .e.dst = &c->rmac, - }); - free(pkt); -} -void tcp_listen( - uint16_t port, - void (*on_conn)(struct tcp_conn *, void *carg), - void (*on_recv)(void *carg), - void (*on_close)(void *carg), - void *carg) -{ - struct tcp_conn *c = malloc(sizeof *c); - memset(c, 0, sizeof *c); - c->lport = port; - c->lip = state.ip; - c->state = LISTEN; - c->on_conn = on_conn; - c->on_recv = on_recv; - c->on_close = on_close; - c->carg = carg; - // TODO setting the ring size super low loses every nth byte. probably a bug with ring_t itself! - c->rx = (ring_t){malloc(4096), 4096, 0, 0}; - conns_append(c); -} -struct tcp_conn *tcpc_new( - struct tcp t, - void (*on_recv)(void *carg), - void (*on_close)(void *carg), - void *carg) -{ - struct tcp_conn *c = malloc(sizeof *c); - memset(c, 0, sizeof *c); - c->lip = t.ip.src ? t.ip.src : state.ip; - c->rip = t.ip.dst; - c->lport = t.src ? t.src : 50002; // TODO randomize source ports - c->rport = t.dst; - if (arpcache_get(c->rip, &c->rmac) < 0) { - // TODO wait for ARP reply - arp_request(c->rip); - if (arpcache_get(state.gateway, &c->rmac) < 0) { - eprintf("neither target nor gateway not in ARP cache, dropping"); - free(c); - return NULL; - } - } - - c->state = SYN_SENT; - c->on_recv = on_recv; - c->on_close = on_close; - c->carg = carg; - c->rx = (ring_t){malloc(4096), 4096, 0, 0}; - conns_append(c); - - tcpc_sendraw(c, FlagSYN, NULL, 0); - c->lseq++; - return c; -} -size_t tcpc_tryread(struct tcp_conn *c, void *buf, size_t len) { - if (!buf) return ring_used(&c->rx); - return ring_get(&c->rx, buf, len); -} -void tcpc_send(struct tcp_conn *c, const void *buf, size_t len) { - tcpc_sendraw(c, FlagACK | FlagPSH, buf, len); -} -static void tcpc_tryfree(struct tcp_conn *c) { - if (c->state == CLOSED && c->uclosed) { - if (c->next) c->next->link = c->link; - *c->link = c->next; - free(c->rx.buf); - free(c); - } -} -void tcpc_close(struct tcp_conn *c) { - /* ONLY FOR USE BY THE USER, drops their reference */ - assert(!c->uclosed); - c->uclosed = true; - if (c->state != CLOSED && c->state != LAST_ACK && c->state != LISTEN) { - tcpc_sendraw(c, FlagFIN | FlagACK, NULL, 0); - c->state = LAST_ACK; - c->on_conn = NULL; - c->on_close = NULL; - } - tcpc_tryfree(c); -} - -void tcp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { - if (len < 20) return; - uint16_t srcport = nget16(buf + SrcPort); - uint16_t dstport = nget16(buf + DstPort); - uint32_t seq = nget32(buf + Seq); - uint32_t acknum = nget32(buf + AckNum); - uint16_t flags = nget16(buf + Flags); - // uint16_t winsize = nget16(buf + WinSize); - // uint16_t chksum = nget16(buf + Checksum); - uint16_t hdrlen = ((flags & FlagSize) >> 12) * 4; - if (hdrlen > len) return; - uint16_t payloadlen = len - hdrlen; - - for (struct tcp_conn *iter = conns; iter; iter = iter->next) { - if (iter->state == CLOSED) continue; - if (iter->lport != dstport) continue; - - if (iter->state == LISTEN && (flags & FlagAll) == FlagSYN) { - iter->state = SYN_RCVD; - iter->rip = ip.src; - iter->rport = srcport; - iter->lack = seq + 1; - memcpy(&iter->rmac, ip.e.src, sizeof(mac_t)); - tcpc_sendraw(iter, FlagSYN | FlagACK, NULL, 0); - iter->lseq++; - if (iter->on_conn) iter->on_conn(iter, iter->carg); - return; - } - - if (iter->rip == ip.src && iter->rport == srcport) { - // TODO doesn't handle seq/ack overflows - if (iter->state == SYN_SENT) { - if (flags & FlagSYN) { - iter->state = ESTABILISHED; - iter->lack = seq + 1; - tcpc_sendraw(iter, FlagACK, NULL, 0); - return; - } else { - // TODO resend syn? - return; - } - } - if (flags & FlagACK) { - if (iter->rack < acknum) - iter->rack = acknum; - if (iter->state == LAST_ACK) { - // TODO check if ack has correct number - iter->state = CLOSED; - tcpc_tryfree(iter); - // TODO free (also after a timeout) - return; - } - } - if (iter->lack != seq && iter->lack - 1 != seq) { - eprintf("remote seq jumped by %d", seq - iter->lack); - tcpc_sendraw(iter, FlagACK, NULL, 0); - return; - } - // TODO check if overflows window size - if (payloadlen) { - iter->lack = seq + payloadlen; - ring_put(&iter->rx, buf + hdrlen, payloadlen); - if (iter->on_recv) iter->on_recv(iter->carg); - tcpc_sendraw(iter, FlagACK, NULL, 0); - } - if (flags & FlagFIN) { - // TODO should resend the packet until an ACK is received - // TODO duplicated in tcpc_close - tcpc_sendraw(iter, FlagFIN | FlagACK, NULL, 0); - iter->state = LAST_ACK; - if (iter->on_close) iter->on_close(iter->carg); - return; - } - return; - } - } - - if ((flags & FlagRST) == 0) { - uint8_t *pkt = malloc(MinHdr); - memset(pkt, 0, MinHdr); - nput16(pkt + SrcPort, dstport); - nput16(pkt + DstPort, srcport); - nput32(pkt + Seq, acknum); - nput32(pkt + AckNum, seq + 1); - uint16_t pktflags = FlagRST | FlagACK; - pktflags |= (MinHdr / 4) << 12; - nput16(pkt + Flags, pktflags); - nput16(pkt + Checksum, ip_checksumphdr(pkt, MinHdr, ip.src, ip.dst, 6)); - - ipv4_send(pkt, MinHdr, (struct ipv4){ - .proto = 6, - .src = ip.dst, - .dst = ip.src, - .e.dst = ip.e.src, - }); - free(pkt); - } -} diff --git a/src/user/app/netstack/udp.c b/src/user/app/netstack/udp.c deleted file mode 100644 index 3d560ae..0000000 --- a/src/user/app/netstack/udp.c +++ /dev/null @@ -1,124 +0,0 @@ -#include "proto.h" -#include "util.h" - -enum { - SrcPort = 0, - DstPort = 2, - Length = 4, - Checksum = 6, - Payload = 8, -}; - - -struct udp_conn { - uint32_t lip, rip; - uint16_t lport, rport; - mac_t rmac; - void (*on_conn)(struct udp_conn *, void *); /* if non-NULL - listening, if NULL - estabilished */ - void (*on_recv)(const void *, size_t, void *); - void *carg; - struct udp_conn *next, **link; -}; -static struct udp_conn *conns; -static void conns_append(struct udp_conn *c) { - c->next = conns; - if (c->next) - c->next->link = &c->next; - c->link = &conns; - *c->link = c; -} -void udp_listen( - uint16_t port, - void (*on_conn)(struct udp_conn *, void *carg), - void (*on_recv)(const void *, size_t, void *carg), - void *carg) -{ - if (!on_conn) return; - struct udp_conn *c = malloc(sizeof *c); - c->lport = port; - c->lip = state.ip; - c->on_conn = on_conn; - c->on_recv = on_recv; - c->carg = carg; - conns_append(c); -} -struct udp_conn *udpc_new( - struct udp u, - void (*on_recv)(const void *, size_t, void *carg), - void *carg) -{ - struct udp_conn *c = malloc(sizeof *c); - memset(c, 0, sizeof *c); - c->lip = u.ip.src; - c->rip = u.ip.dst; - c->lport = u.src ? u.src : 50000; // TODO randomize source ports - c->rport = u.dst; - if (arpcache_get(c->rip, &c->rmac) < 0) { - // TODO make arp request, wait for reply - eprintf("not in ARP cache, unimplemented"); - free(c); - return NULL; - } - c->on_recv = on_recv; - c->carg = carg; - conns_append(c); - return c; -} -void udpc_send(struct udp_conn *c, const void *buf, size_t len) { - uint8_t *pkt = malloc(Payload + len); - nput16(pkt + SrcPort, c->lport); - nput16(pkt + DstPort, c->rport); - nput16(pkt + Length, Payload + len); - nput16(pkt + Checksum, 0); - memcpy(pkt + Payload, buf, len); - ipv4_send(pkt, Payload + len, (struct ipv4){ - .proto = 0x11, - .src = c->lip, - .dst = c->rip, - .e.dst = &c->rmac, - }); - free(pkt); -} -void udpc_close(struct udp_conn *c) { - if (c->next) c->next->link = c->link; - *(c->link) = c->next; - free(c); -} - - -void udp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { - uint16_t srcport = nget16(buf + SrcPort); - uint16_t dstport = nget16(buf + DstPort); - bool active = false; - - for (struct udp_conn *iter = conns; iter; iter = iter->next) { - if (iter->on_conn && dstport == iter->lport) { - iter->on_conn(iter, iter->carg); - iter->on_conn = NULL; - iter->rport = srcport; - memcpy(&iter->rmac, ip.e.src, sizeof(mac_t)); - iter->rip = ip.src; - } - if (iter->rip == ip.src && - iter->rport == srcport && - iter->lport == dstport && - iter->on_recv) - { - active = true; - iter->on_recv(buf + Payload, len - Payload, iter->carg); - } - } - - if (!active) { - uint8_t *pkt = malloc(4 + ip.hlen + 8); - nput32(pkt, 0); - memcpy(pkt + 4, ip.header, ip.hlen + 8); - icmp_send(pkt, 4 + ip.hlen + 8, (struct icmp){ - .type = 3, /* destination unreachable */ - .code = 3, /* port unreachable */ - .ip.dst = ip.src, - .ip.e.dst = ip.e.src, - }); - free(pkt); - } -} diff --git a/src/user/app/netstack/util.c b/src/user/app/netstack/util.c deleted file mode 100644 index 68092aa..0000000 --- a/src/user/app/netstack/util.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "util.h" - -/* https://www.w3.org/TR/PNG/#D-CRCAppendix */ -static uint32_t crc_table[256]; -uint32_t crc32(const uint8_t *buf, size_t len) { - if (!crc_table[1]) { - for (int i = 0; i < 256; i++) { - uint32_t c = i; - for (int j = 0; j < 8; j++) - c = ((c&1) ? 0xedb88320 : 0) ^ (c >> 1); - crc_table[i] = c; - } - } - - uint32_t c = 0xFFFFFFFF; - for (size_t i = 0; i < len; i++) - c = crc_table[(c ^ buf[i]) & 0xff] ^ (c >> 8); - return ~c; -} - -uint16_t ip_checksum(const uint8_t *buf, size_t len) { - uint32_t c = 0; - while (len >= 2) { - c += nget16(buf); - buf += 2; len -= 2; - } - if (len) c += (*buf) << 8; - while (c > 0xFFFF) c = (c & 0xFFFF) + (c >> 16); - return ~c; -} - -uint16_t ip_checksumphdr( - const uint8_t *buf, size_t len, - uint32_t ip1, uint32_t ip2, - uint16_t proto) -{ - uint32_t c = (uint16_t)~ip_checksum(buf, len); - c += (ip1 & 0xFFFF) + (ip1 >> 16); - c += (ip2 & 0xFFFF) + (ip2 >> 16); - c += proto + len; - while (c > 0xFFFF) c = (c & 0xFFFF) + (c >> 16); - return ~c; -} - -int ip_parse(const char *s, uint32_t *dest) { - if (!s) return -1; - - uint32_t ip = strtol(s, (char**)&s, 0); - int parts = 1; - for (; parts < 4; parts++) { - if (!*s) break; - if (*s++ != '.') return -1; - ip <<= 8; - ip += strtol(s, (char**)&s, 0); - } - if (parts > 1) - ip = ((ip & 0xFFFFFF00) << 8 * (4 - parts)) | (ip & 0xFF); - *dest = ip; - return 0; -} diff --git a/src/user/app/netstack/util.h b/src/user/app/netstack/util.h deleted file mode 100644 index 0b29560..0000000 --- a/src/user/app/netstack/util.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define eprintf(fmt, ...) fprintf(stderr, "netstack: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - -uint32_t crc32(const uint8_t *buf, size_t len); -uint16_t ip_checksum(const uint8_t *buf, size_t len); -uint16_t ip_checksumphdr( - const uint8_t *buf, size_t len, - uint32_t ip1, uint32_t ip2, - uint16_t proto); -/* 0 on success, negative failure */ -int ip_parse(const char *s, uint32_t *ip); - -static inline void nput16(void *vbuf, uint16_t n) { - uint8_t *b = vbuf; - b[0] = n >> 8; - b[1] = n >> 0; -} - -static inline void nput32(void *vbuf, uint32_t n) { - uint8_t *b = vbuf; - b[0] = n >> 24; - b[1] = n >> 16; - b[2] = n >> 8; - b[3] = n >> 0; -} - -static inline uint16_t nget16(const void *vbuf) { - const uint8_t *b = vbuf; - return (b[0] << 8) - | (b[1] << 0); -} - -static inline uint32_t nget32(const void *vbuf) { - const uint8_t *b = vbuf; - return (b[0] << 24) - | (b[1] << 16) - | (b[2] << 8) - | (b[3] << 0); -} diff --git a/src/user/app/ps/ps.c b/src/user/app/ps/ps.c deleted file mode 100644 index 76a5841..0000000 --- a/src/user/app/ps/ps.c +++ /dev/null @@ -1,54 +0,0 @@ -#include <_proc.h> -#include <ctype.h> -#include <err.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int -main(void) -{ - char *readbuf = malloc(4096); - char *procbuf = malloc(4096); - FILE *f = fopen("/proc/", "r"); - if (!f) { - err(1, "couldn't open /proc/"); - } - - // TODO library for iterating over directories - for (;;) { - int len = fread(readbuf, 1, 4096, f); - if (len <= 0) break; - for (int pos = 0; pos < len; ) { - char *end = memchr(readbuf + pos, 0, len - pos); - if (!end) { - errx(1, "unimplemented: buffer overflow"); - } - size_t entryl = end - (readbuf+pos) + 1; - if (isdigit(readbuf[pos])) { - FILE *g; - sprintf(procbuf, "/proc/%smem", readbuf+pos); - g = fopen(procbuf, "r"); - if (!g) { - warn("couldn't open \"%s\"", procbuf); - strcpy(procbuf, "(can't peek)"); - } else { - fseek(g, (long)&_psdata_loc->desc, SEEK_SET); - if (fread(procbuf, 1, 128, g) <= 0) { - strcpy(procbuf, "(no psdata)"); - } - procbuf[128] = '\0'; - fclose(g); - } - end[-1] = '\0'; /* remove trailing slash */ - printf("%s\t%s\n", readbuf+pos, procbuf); - } - pos += entryl; - } - } - - free(readbuf); - free(procbuf); - fclose(f); - return 0; -} diff --git a/src/user/app/shell/builtins.c b/src/user/app/shell/builtins.c deleted file mode 100644 index 9c294b2..0000000 --- a/src/user/app/shell/builtins.c +++ /dev/null @@ -1,254 +0,0 @@ -#include "builtins.h" -#include "shell.h" -#include <camellia.h> -#include <camellia/fs/misc.h> -#include <camellia/path.h> -#include <err.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#define DEFAULT_ARGV(...) \ - char *_argv_default[] = {argv[0], __VA_ARGS__}; \ - if (argc <= 1) { \ - argc = sizeof(_argv_default) / sizeof(*_argv_default); \ - argv = _argv_default; \ - } - -static void cmd_cat(int argc, char **argv) { - const size_t buflen = 4096; - char *buf = malloc(buflen); - - DEFAULT_ARGV("!stdin"); - for (int i = 1; i < argc; i++) { - FILE *file = fopen(argv[i], "r"); - if (!file) { - perror(argv[i]); - return; - } - if (!strcmp(argv[i], "!stdin")) fextflags(file, FEXT_NOFILL); - for (;;) { - int len = fread(buf, 1, buflen, file); - if (len <= 0) break; - fwrite(buf, 1, len, stdout); - } - if (ferror(file)) { - perror(argv[i]); - return; - } - fclose(file); - } -} - -static void cmd_echo(int argc, char **argv) { - bool newline = true; - int i = 1; - - if (!strcmp("-n", argv[i])) { - i++; - newline = false; - } - - printf("%s", argv[i++]); - for (; i < argc; i++) - printf(" %s", argv[i]); - if (newline) - printf("\n"); -} - -void cmd_getsize(int argc, char **argv) { - if (argc < 2) errx(1, "no arguments"); - for (int i = 1; i < argc; i++) { - hid_t h = camellia_open(argv[i], OPEN_READ); - if (h < 0) { - warn("error opening %s", argv[i]); - continue; - } - printf("%s: %d\n", argv[i], (int)_sys_getsize(h)); - _sys_close(h); - } -} - -void cmd_hexdump(int argc, char **argv) { - DEFAULT_ARGV("!stdin"); - const size_t buflen = 4096; - uint8_t *buf = malloc(buflen); - FILE *file; - bool canonical = strcmp(argv[0], "hd") == 0; - size_t readlen = ~0; - size_t pos = 0; - bool raw = false; - - int c; - optind = 0; - while ((c = getopt(argc, argv, "Cn:s:r")) != -1) { - switch (c) { - case 'C': - canonical = true; - break; - case 'n': - readlen = strtol(optarg, NULL, 0); - break; - case 's': - pos = strtol(optarg, NULL, 0); - break; - case 'r': /* "raw" mode, print data as soon as it's read without buffering */ - raw = true; - break; - default: - return; - } - } - if (readlen != (size_t)~0) - readlen += pos; - - for (; optind < argc; optind++) { - file = fopen(argv[optind], "r"); - if (!file) { - warn("couldn't open %s", argv[optind]); - continue; - } - if (raw) fextflags(file, FEXT_NOFILL); - fseek(file, pos, SEEK_SET); - bool skipped = false; - while (pos < readlen) { - size_t len = buflen; - if (len > readlen - pos) - len = readlen - pos; - len = fread(buf, 1, len, file); - if (len == 0) break; - - for (size_t i = 0; i < len; i += 16) { - if (i >= 16 && !memcmp(buf + i, buf + i - 16, 16)) { - if (!skipped) { - printf("*\n"); - skipped = true; - } - continue; - } else skipped = false; - printf("%08zx ", pos + i); - - for (size_t j = i; j < i + 8 && j < len; j++) - printf("%02x ", buf[j]); - printf(" "); - for (size_t j = i + 8; j < i + 16 && j < len; j++) - printf("%02x ", buf[j]); - - if (canonical) { - printf(" |"); - - for (size_t j = i; j < i + 16 && j < len; j++) { - char c = '.'; - if (0x20 <= buf[j] && buf[j] < 0x7f) c = buf[j]; - printf("%c", c); - } - printf("|\n"); - } else { - printf("\n"); - } - } - pos += len; - } - printf("%08zx\n", pos); - fclose(file); - } -} - -static void cmd_ls(int argc, char **argv) { - FILE *file; - const size_t buflen = 4096; - char *buf = malloc(buflen); - - DEFAULT_ARGV("."); - for (int i = 1; i < argc; i++) { - char *path = (void*)argv[i]; - int pathlen = strlen(path); - - if (!pathlen || path[pathlen - 1] != '/') { - memcpy(buf, path, pathlen); - buf[pathlen] = '/'; - buf[pathlen+1] = '\0'; - path = buf; - } - - file = fopen(path, "r"); - if (!file) { - warn("couldn't open %s", argv[i]); - continue; - } - for (;;) { - int len = fread(buf, 1, buflen, file); - if (len <= 0) break; - for (int i = 0; i < len; i++) - if (buf[i] == '\0') buf[i] = '\n'; - fwrite(buf, 1, len, stdout); - } - fclose(file); - } -} - -static void cmd_mkdir(int argc, char **argv) { - // TODO mkdir -p - if (argc < 2) errx(1, "no arguments"); - for (int i = 1; i < argc; i++) { - if (mkdir(argv[i], 0777) < 0) - perror(argv[i]); - } -} - -static void cmd_rm(int argc, char **argv) { - if (argc < 2) errx(1, "no arguments"); - for (int i = 1; i < argc; i++) { - if (unlink(argv[i]) < 0) - perror(argv[i]); - } -} - -static void cmd_sleep(int argc, char **argv) { - if (argc < 2) errx(1, "no arguments"); - _sys_sleep(strtol(argv[1], NULL, 0) * 1000); -} - -static void cmd_touch(int argc, char **argv) { - if (argc < 2) errx(1, "no arguments"); - for (int i = 1; i < argc; i++) { - FILE *f = fopen(argv[i], "a"); - if (!f) perror(argv[i]); - fclose(f); - } -} - -static void cmd_whitelist(int argc, char **argv) { - int split = 1; - for (; split < argc;) { - if (!strcmp("--", argv[split])) break; - split++; - } - argv[split] = NULL; - MOUNT_AT("/") { fs_whitelist((void*)&argv[1]); } - - if (split < argc) { - run_args(argc - split - 1, &argv[split + 1], NULL); - } else { - const char **argv = (const char*[]){"shell", NULL}; - run_args(1, (void*)argv, NULL); - } -} - -struct builtin builtins[] = { - {"cat", cmd_cat}, - {"echo", cmd_echo}, - {"getsize", cmd_getsize}, - {"hd", cmd_hexdump}, - {"hexdump", cmd_hexdump}, - {"ls", cmd_ls}, - {"mkdir", cmd_mkdir}, - {"rm", cmd_rm}, - {"sleep", cmd_sleep}, - {"touch", cmd_touch}, - {"whitelist", cmd_whitelist}, - {NULL, NULL}, -}; diff --git a/src/user/app/shell/builtins.h b/src/user/app/shell/builtins.h deleted file mode 100644 index 1e422bb..0000000 --- a/src/user/app/shell/builtins.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include <camellia/syscalls.h> -#include <stdbool.h> - -struct builtin { - const char *name; - void (*fn)(int argc, char **argv); -}; - -extern struct builtin builtins[]; diff --git a/src/user/app/shell/parser.c b/src/user/app/shell/parser.c deleted file mode 100644 index ad09348..0000000 --- a/src/user/app/shell/parser.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "shell.h" -#include <ctype.h> -#include <stdbool.h> -#include <string.h> - -static char skipspace(char **sp) { - char *s = *sp; - while (*s && isspace(*s)) s++; - *sp = s; - return *s; -} - -static bool isspecial(char c) { - return c == '>' || c == '#'; -} - -static char *parg(char **sp) { - char *s = *sp; - char *res = NULL; - - if (skipspace(&s)) { - // TODO incorrectly handles strings like a"b" - switch (*s) { - case '"': - s++; - res = s; - while (*s && *s != '"') - s++; - break; - default: - res = s; - while (*s && !isspace(*s) && !isspecial(*s)) - s++; - if (*s == '#') { - *s = '\0'; /* end parsing early */ - if (res == s) /* don't output an empty arg */ - res = NULL; - } - break; - } - if (*s) *s++ = '\0'; - } - - *sp = s; - return res; -} - -int parse(char *s, char **argv, size_t argvlen, struct redir *redir) { - if (argvlen == 0) return -1; - size_t argc = 0; - char *arg; - - *argv = NULL; - redir->stdout = NULL; - redir->append = false; - - while (skipspace(&s)) { - switch (*s) { - case '>': - s++; - if (*s == '>') { - s++; - redir->append = true; - } - redir->stdout = parg(&s); - break; - default: - arg = parg(&s); - argv[argc++] = arg; - if (argc >= argvlen) - return -1; - } - } - argv[argc] = NULL; - return argc; -} diff --git a/src/user/app/shell/shell.c b/src/user/app/shell/shell.c deleted file mode 100644 index 185aa7e..0000000 --- a/src/user/app/shell/shell.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "builtins.h" -#include "shell.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <err.h> -#include <errno.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <camellia/fs/misc.h> -#include <x86intrin.h> - -int main(); - -static void execp(char **argv) { - if (!argv || !*argv) return; - if (strncmp(argv[0], "/", 1) == 0 || - strncmp(argv[0], "./", 2) == 0 || - strncmp(argv[0], "../", 3) == 0) - { - execv(argv[0], argv); - } else { - size_t cmdlen = strlen(argv[0]); - char *s = malloc(cmdlen + 6); - if (!s) err(1, "malloc"); - memcpy(s, "/bin/", 5); - memcpy(s + 5, argv[0], cmdlen + 1); - execv(s, argv); - free(s); - } -} - -void run_args(int argc, char **argv, struct redir *redir) { - if (!*argv) return; - - /* "special" commands that can't be handled in a subprocess */ - if (!strcmp(argv[0], "mount")) { - if (argc < 3) { - fprintf(stderr, "mount: not enough arguments\n"); - return; - } - MOUNT_AT("/") { - fs_dirinject(argv[1]); - } - MOUNT_AT(argv[1]) { - run_args(argc - 2, argv + 2, redir); - exit(1); - } - return; - } else if (!strcmp(argv[0], "shadow")) { - if (argc < 2) { - fprintf(stderr, "shadow: missing path\n"); - } else { - _sys_mount(HANDLE_NULLFS, argv[1], strlen(argv[1])); - } - } else if (!strcmp(argv[0], "procmnt")) { - if (argc < 2) { - fprintf(stderr, "procmnt: missing mountpoint\n"); - return; - } - _sys_mount(HANDLE_PROCFS, argv[1], strlen(argv[1])); - /* - if (!(3 <= argc && !strcmp(argv[2], "raw"))) { - if (!mount_at("/")) { - fs_dirinject(argv[1]); - exit(1); - } - } - */ - return; - } else if (!strcmp(argv[0], "cd")) { - if (chdir(argc > 1 ? argv[1] : "/") < 0) - perror("cd"); - return; - } else if (!strcmp(argv[0], "time")) { - uint64_t time = __rdtsc(); - uint64_t div = 3000; - run_args(argc - 1, argv + 1, redir); - time = __rdtsc() - time; - printf("%lu ns (assuming 3GHz)\n", time / div); - return; - } else if (!strcmp(argv[0], "exit")) { - exit(0); - } else if (!strcmp(argv[0], "getpid")) { - printf("my\t%d\nparent\t%d\n", getpid(), getppid()); - return; - } - - if (fork()) { - _sys_await(); - return; - } - - if (redir && redir->stdout) { - FILE *f = fopen(redir->stdout, redir->append ? "a" : "w"); - if (!f) { - err(1, "couldn't open %s for redirection", redir->stdout); - } - - /* a workaround for file offsets not being preserved across exec()s. - * TODO document that weird behaviour of exec() */ - hid_t p[2]; - if (_sys_pipe(p, 0) < 0) { - errx(1, "couldn't create redirection pipe"); - } - if (!_sys_fork(FORK_NOREAP, NULL)) { - /* the child forwards data from the pipe to the file */ - const size_t buflen = 512; - char *buf = malloc(buflen); - if (!buf) err(1, "when redirecting"); - close(p[1]); - for (;;) { - long len = _sys_read(p[0], buf, buflen, 0); - if (len < 0) exit(0); - fwrite(buf, 1, len, f); - if (ferror(f)) exit(0); - } - } - - fclose(f); - close(p[0]); - if (_sys_dup(p[1], 1, 0) < 0) { - errx(1, "dup() failed when redirecting"); - } - } - - for (struct builtin *iter = builtins; iter->name; iter++) { - if (!strcmp(argv[0], iter->name)) { - setprogname(argv[0]); - iter->fn(argc, argv); - exit(0); - } - } - - execp(argv); - if (errno == EINVAL) { - errx(1, "%s isn't a valid executable", argv[0]); - } else { - errx(1, "unknown command: %s", argv[0]); - } -} - -static void run(char *cmd) { -#define ARGV_MAX 16 - char *argv[ARGV_MAX]; - struct redir redir; - int argc = parse(cmd, argv, ARGV_MAX, &redir); - if (argc < 0) { - warn("parsing error"); - } else { - run_args(argc, argv, &redir); - } -} - -int main(int argc, char **argv) { - static char buf[256]; - FILE *f = stdin; - - if (argc > 1) { - f = fopen(argv[1], "r"); - if (!f) { - err(1, "couldn't open %s", argv[1]); - return 1; - } - } - - for (;;) { - if (f == stdin) { - printf("%s $ ", getcwd(buf, sizeof buf)); - } - if (!fgets(buf, 256, f)) { - return 0; - } - run(buf); - } -} diff --git a/src/user/app/shell/shell.h b/src/user/app/shell/shell.h deleted file mode 100644 index 050e704..0000000 --- a/src/user/app/shell/shell.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include <stdbool.h> -#include <stddef.h> - -struct redir { - const char *stdout; - bool append; -}; - -int parse(char *s, char **argv, size_t argvlen, struct redir *redir); -void run_args(int argc, char **argv, struct redir *redir); diff --git a/src/user/app/testelf/main.c b/src/user/app/testelf/main.c deleted file mode 100644 index 0cbe56c..0000000 --- a/src/user/app/testelf/main.c +++ /dev/null @@ -1,26 +0,0 @@ -#include <err.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -const char *str = "Hello!", *str2 = "World."; - -int main(int argc, char **argv) { - printf("elftest's &main == %p\n", &main); - printf("%s %s\n", str, str2); - printf("argc == %u\n", argc); - for (int i = 0; i < argc; i++) - printf("argv[%u] == %p == \"%s\"\n", i, argv[i], argv[i]); - if (argv[1] && strcmp(argv[1], "stackexec") == 0) { - /* exec something with arguments on the stack */ - const char s_d[] = "I am a pretty long string on the stack. Oh my. " \ - "I hope I won't get corrupted.\0"; - char s[sizeof(s_d)]; - memcpy(s, s_d, sizeof(s_d)); - const char *argv2[] = {"/bin/testelf", s, s, "hello", s, s, s, "lol", NULL}; - printf("argv2 == %p, s == %p\n== exec ==\n", argv2, s); - execv((void*)argv2[0], (void*)argv2); - err(1, "execv"); - } - return 0; -} diff --git a/src/user/app/tests/kernel/fdlimit.c b/src/user/app/tests/kernel/fdlimit.c deleted file mode 100644 index f332357..0000000 --- a/src/user/app/tests/kernel/fdlimit.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "../tests.h" -#include <camellia/flags.h> -#include <errno.h> -#include <unistd.h> - -static void test_fdlimit_pipe(void) { - hid_t ends[2]; - hid_t h[2] = {-1, -1}; - for (;;) { - hid_t cur = _sys_open("/", 1, OPEN_READ); - if (cur == -EMFILE) break; - test(cur >= 0); - h[0] = h[1]; - h[1] = cur; - } - test(h[0] >= 0); /* we need at least two handles */ - - test(_sys_pipe(ends, 0) == -EMFILE); - test(_sys_open("/", 1, OPEN_READ) == -EMFILE); - - close(h[1]); - test(_sys_pipe(ends, 0) == -EMFILE); - test(_sys_open("/", 1, OPEN_READ) == h[1]); - test(_sys_open("/", 1, OPEN_READ) == -EMFILE); - - close(h[0]); - close(h[1]); - test(_sys_pipe(ends, 0) == 0); -} - -static void test_fdlimit_fork(void) { - for (;;) { - hid_t cur = _sys_open("/", 1, OPEN_READ); - if (cur == -EMFILE) break; - test(cur >= 0); - } - - if (!_sys_fork(0, NULL)) _sys_exit(123); - - test(_sys_fork(FORK_NEWFS, NULL) == -EMFILE); - test(_sys_await() == 123); - test(_sys_await() == ~0); -} - -void r_k_fdlimit(void) { - /* all these tests implicitly test if the vfs returns -EMFILE */ - run_test(test_fdlimit_pipe); - run_test(test_fdlimit_fork); -} diff --git a/src/user/app/tests/kernel/fs.c b/src/user/app/tests/kernel/fs.c deleted file mode 100644 index b5684d7..0000000 --- a/src/user/app/tests/kernel/fs.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "../tests.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdbool.h> -#include <string.h> - -static void test_unfinished_req(void) { - hid_t h = -1; - int ret = _sys_fork(FORK_NEWFS, &h); - test(0 <= ret); - if (ret == 0) { - // TODO make a similar test with all 0s passed to fs_wait - struct ufs_request res; - _sys_fs_wait(NULL, 0, &res); - // TODO second fs_wait - exit(0); - } else { - test(0 <= h); - test(_sys_mount(h, "/", 1) == 0); - int ret = _sys_open("/", 1, 0); - test(ret < 0); - // the handler quits while handling that call - but this syscall should return anyways - } -} - -static void test_orphaned_fs(void) { - hid_t h = -1; - int ret = _sys_fork(FORK_NEWFS, &h); - test(0 <= ret); - if (ret == 0) { - exit(0); - } else { - test(0 <= h); - test(_sys_mount(h, "/", 1) == 0); - int ret = _sys_open("/", 1, 0); - test(ret < 0); - } -} - -static void test_fs_cleanup(void) { - const char *msg = "success"; - int msglen = 8; - char buf[16]; - hid_t ends[2]; - test(_sys_pipe(ends, 0) >= 0); - if (!_sys_fork(0, NULL)) { - hid_t h = -1; - if (_sys_fork(FORK_NEWFS, &h) == 0) { - for (;;) { - struct ufs_request req; - hid_t reqh = _sys_fs_wait(buf, sizeof buf, &req); - if (reqh < 0) break; - _sys_fs_respond(reqh, NULL, 0, 0); /* success */ - } - /* this is the test: does it break out of the loop - * when it should cleanup */ - _sys_write(ends[1], msg, msglen, -1, 0); - exit(0); - } else { - test(_sys_mount(h, "/", 1) == 0); - h = _sys_open("/", 1, 0); - test(h >= 0); - _sys_close(h); - // TODO another test without the delay - _sys_sleep(0); - exit(0); - } - } else { - test(_sys_read(ends[0], buf, sizeof buf, 0) == msglen); - test(memcmp(buf, msg, msglen) == 0); - } -} - -void r_k_fs(void) { - run_test(test_unfinished_req); - run_test(test_orphaned_fs); - run_test(test_fs_cleanup); - run_test(test_fs_cleanup); -} diff --git a/src/user/app/tests/kernel/misc.c b/src/user/app/tests/kernel/misc.c deleted file mode 100644 index 5d6b531..0000000 --- a/src/user/app/tests/kernel/misc.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "../tests.h" -#include <camellia/errno.h> -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <string.h> -#include <unistd.h> - -static void test_fault_kill(void) { - if (!fork()) { /* invalid memory access */ - asm volatile("movb $69, 0" ::: "memory"); - test_fail(); - } - if (!fork()) { /* #GP */ - asm volatile("hlt" ::: "memory"); - test_fail(); - } - - int await_cnt = 0; - while (_sys_await() != ~0) await_cnt++; - test(await_cnt == 2); -} - -static void test_efault(void) { -#if 0 - const char *str = "o, 16 characters"; - char str2[16]; - char *invalid = (void*)0x1000; - hid_t h; - - memcpy(str2, str, 16); - - test((h = _sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE | OPEN_WRITE)) >= 0); - test(_sys_write(h, str, 16, 0, 0) == 16); - test(_sys_write(h, str2, 16, 0, 0) == 16); - - test(_sys_write(h, invalid, 16, 0, 0) == -EFAULT); - - /* x64 non-canonical pointers */ - test(_sys_write(h, (void*)((uintptr_t)str ^ 0x8000000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str ^ 0x1000000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0100000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0010000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0001000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0000800000000000), 16, 0, 0) == -EFAULT); - - test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x8000000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x1000000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0100000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0010000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0001000000000000), 16, 0, 0) == -EFAULT); - test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0000800000000000), 16, 0, 0) == -EFAULT); - - test(_sys_write(h, str, 16, 0, 0) == 16); - close(h); -#endif -} - -static void test_invalid_syscall(void) { - test(_syscall(~0, 0, 0, 0, 0, 0) < 0); -} - -void r_k_misc(void) { - run_test(test_fault_kill); - run_test(test_efault); - run_test(test_invalid_syscall); -} diff --git a/src/user/app/tests/kernel/miscsyscall.c b/src/user/app/tests/kernel/miscsyscall.c deleted file mode 100644 index 459da2a..0000000 --- a/src/user/app/tests/kernel/miscsyscall.c +++ /dev/null @@ -1,315 +0,0 @@ -#include "../tests.h" -#include <camellia/errno.h> -#include <camellia/execbuf.h> -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <string.h> -#include <unistd.h> - - -static void test_await(void) { - /* creates 16 child processes, each returning a different value. then checks - * if await() returns every value exactly once */ - int ret; - int counts[16] = {0}; - - for (int i = 0; i < 16; i++) - if (!fork()) - exit(i); - - while ((ret = _sys_await()) != ~0) { - test(0 <= ret && ret < 16); - counts[ret]++; - } - - for (int i = 0; i < 16; i++) - test(counts[i] == 1); -} - -static void test_await2(void) { - /* hangs on failure */ - if (!fork()) { - if (!fork()) { - for (;;) _sys_sleep(1000000000); - } else { - exit(123); - } - } - test(_sys_await() == 123); - test(_sys_await() == ~0); /* don't wait for tombstone */ - _sys_filicide(); -} - -static void test_wait2_basic(void) { - struct sys_wait2 data = {0}; - int pid; - pid = fork(); - if (pid == 0) { - exit(420); - } - test(_sys_wait2(-1, 0, &data) == pid); - test(data.status == 420); - test(_sys_wait2(-1, 0, NULL) == -ECHILD); -} - -static void test_pipe(void) { - const char *pipe_msgs[2] = {"hello", "world"}; - hid_t ends[2]; - char buf[16]; - - /* test regular reads / writes */ - test(_sys_pipe(ends, 0) >= 0); - if (!fork()) { - /* those repeated asserts ensure that you can't read/write to the wrong ends */ - test(_sys_read(ends[1], buf, 16, 0) < 0); - test(_sys_write(ends[0], buf, 16, 0, 0) < 0); - - test(5 == _sys_write(ends[1], pipe_msgs[0], 5, -1, 0)); - - test(_sys_read(ends[1], buf, 16, 0) < 0); - test(_sys_write(ends[0], buf, 16, 0, 0) < 0); - - test(5 == _sys_write(ends[1], pipe_msgs[1], 5, -1, 0)); - - exit(0); - } else { - test(_sys_read(ends[1], buf, 16, 0) < 0); - test(_sys_write(ends[0], buf, 16, 0, 0) < 0); - - test(5 == _sys_read(ends[0], buf, 16, 0)); - test(!memcmp(buf, pipe_msgs[0], 5)); - - test(_sys_read(ends[1], buf, 16, 0) < 0); - test(_sys_write(ends[0], buf, 16, 0, 0) < 0); - - test(5 == _sys_read(ends[0], buf, 16, 0)); - test(!memcmp(buf, pipe_msgs[1], 5)); - - _sys_await(); - } - close(ends[0]); - close(ends[1]); - - - /* writing to pipes with one end closed */ - test(_sys_pipe(ends, 0) >= 0); - for (int i = 0; i < 16; i++) { - if (!fork()) { - close(ends[1]); - test(_sys_read(ends[0], buf, 16, 0) < 0); - exit(0); - } - } - close(ends[1]); - for (int i = 0; i < 16; i++) - _sys_await(); - close(ends[0]); - - test(_sys_pipe(ends, 0) >= 0); - for (int i = 0; i < 16; i++) { - if (!fork()) { - close(ends[0]); - test(_sys_write(ends[1], buf, 16, 0, 0) < 0); - exit(0); - } - } - close(ends[0]); - for (int i = 0; i < 16; i++) - _sys_await(); - close(ends[1]); - - - /* queueing */ - test(_sys_pipe(ends, 0) >= 0); - for (int i = 0; i < 16; i++) { - if (!fork()) { - test(_sys_write(ends[1], pipe_msgs[0], 5, -1, 0) == 5); - exit(0); - } - } - close(ends[1]); - for (int i = 0; i < 16; i++) { - test(_sys_read(ends[0], buf, sizeof buf, 0) == 5); - _sys_await(); - } - test(_sys_read(ends[0], buf, sizeof buf, 0) < 0); - close(ends[0]); - - test(_sys_pipe(ends, 0) >= 0); - for (int i = 0; i < 16; i++) { - if (!fork()) { - memset(buf, 0, sizeof buf); - test(_sys_read(ends[0], buf, 5, -1) == 5); - test(!memcmp(buf, pipe_msgs[1], 5)); - exit(0); - } - } - close(ends[0]); - for (int i = 0; i < 16; i++) { - test(_sys_write(ends[1], pipe_msgs[1], 5, -1, 0) == 5); - _sys_await(); - } - test(_sys_write(ends[1], pipe_msgs[1], 5, -1, 0) < 0); - close(ends[1]); - - - // not a to.do detect when all processes that can read are stuck on writing to the pipe and vice versa - // it seems like linux just lets the process hang endlessly. -} - -static void test_memflag(void) { - void *page = (void*)0x77777000; - _sys_memflag(page, 4096, MEMFLAG_PRESENT); // allocate page - memset(page, 77, 4096); // write to it - _sys_memflag(page, 4096, 0); // free it - - if (!fork()) { - memset(page, 11, 4096); // should segfault - exit(0); - } else { - test(_sys_await() != 0); // test if the process crashed - } - - char *pages[4]; - for (size_t i = 0; i < 4; i++) { - pages[i] = _sys_memflag(NULL, 4096, MEMFLAG_FINDFREE); - test(pages[i] == _sys_memflag(NULL, 4096, MEMFLAG_FINDFREE | MEMFLAG_PRESENT)); - if (i > 0) test(pages[i] != pages[i-1]); - *pages[i] = 0x77; - } - - for (size_t i = 0; i < 4; i++) - _sys_memflag(pages, 4096, MEMFLAG_PRESENT); -} - -static void test_dup(void) { - hid_t pipe[2]; - hid_t h1, h2; - test(_sys_pipe(pipe, 0) >= 0); - - if (!fork()) { - close(pipe[0]); - - h1 = _sys_dup(pipe[1], -1, 0); - test(h1 >= 0); - test(h1 != pipe[1]); - h2 = _sys_dup(h1, -1, 0); - test(h2 >= 0); - test(h2 != pipe[1] && h2 != h1); - - _sys_write(pipe[1], "og", 2, 0, 0); - _sys_write(h1, "h1", 2, 0, 0); - _sys_write(h2, "h2", 2, 0, 0); - - close(pipe[1]); - _sys_write(h1, "h1", 2, 0, 0); - _sys_write(h2, "h2", 2, 0, 0); - - test(_sys_dup(h1, pipe[1], 0) == pipe[1]); - test(_sys_dup(h2, pipe[1], 0) == pipe[1]); - test(_sys_dup(h1, pipe[1], 0) == pipe[1]); - test(_sys_dup(h2, pipe[1], 0) == pipe[1]); - close(h1); - close(h2); - - test(_sys_dup(pipe[1], h2, 0) == h2); - _sys_write(h2, "h2", 2, 0, 0); - close(h2); - - test(_sys_dup(pipe[1], h1, 0) == h1); - _sys_write(h1, "h1", 2, 0, 0); - close(h1); - - exit(0); - } else { - char buf[16]; - size_t count = 0; - close(pipe[1]); - while (_sys_read(pipe[0], buf, sizeof buf, 0) >= 0) - count++; - test(count == 7); - _sys_await(); - } - - - close(pipe[0]); -} - -static void test_execbuf(void) { - if (!fork()) { - uint64_t buf[] = { - EXECBUF_SYSCALL, _SYS_EXIT, 123, 0, 0, 0, 0, - }; - _sys_execbuf(buf, sizeof buf); - test_fail(); - } else { - test(_sys_await() == 123); - } -} - -static void test_sleep(void) { - hid_t reader; - FILE *writer; - if (!forkpipe(&writer, &reader)) { - if (!fork()) { - if (!fork()) { - _sys_sleep(100); - fprintf(writer, "1"); - _sys_sleep(200); - fprintf(writer, "3"); - _sys_sleep(200); - fprintf(writer, "5"); - _sys_exit(0); - } - if (!fork()) { - fprintf(writer, "0"); - _sys_sleep(200); - fprintf(writer, "2"); - _sys_sleep(200); - fprintf(writer, "4"); - /* get killed while asleep - * a peaceful death, i suppose. */ - for (;;) _sys_sleep(1000000000); - } - _sys_await(); - _sys_filicide(); - _sys_exit(0); - } - - /* this part checks if, after killing an asleep process, - * other processes can still wake up */ - _sys_sleep(600); - fprintf(writer, "6"); - exit(0); - } else { - const char *expected = "0123456"; - size_t target = strlen(expected); - size_t pos = 0; - for (;;) { - char buf[128]; - long ret = _sys_read(reader, buf, sizeof buf, 0); - if (ret < 0) break; - test(pos + ret <= target); - test(memcmp(buf, expected + pos, ret) == 0); - pos += ret; - } - test(pos == target); - } -} - -static void test_badopen(void) { - test(_sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE | OPEN_WRITE) >= 0); - test(_sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE) == -EINVAL); -} - -void r_k_miscsyscall(void) { - run_test(test_await); - run_test(test_await2); - run_test(test_wait2_basic); - run_test(test_pipe); - run_test(test_memflag); - run_test(test_dup); - run_test(test_execbuf); - run_test(test_sleep); - run_test(test_badopen); -} diff --git a/src/user/app/tests/kernel/path.c b/src/user/app/tests/kernel/path.c deleted file mode 100644 index 5a22c36..0000000 --- a/src/user/app/tests/kernel/path.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "../tests.h" -#include <camellia/path.h> -#include <string.h> -#include <camellia/compat.h> -#include <camellia/fs/misc.h> - -static void test_path_simplify(void) { - const char *testcases[][2] = { - {"/", "/"}, - {"/.", "/"}, - {"//", "/"}, - {"/asdf", "/asdf"}, - {"/asdf/", "/asdf/"}, - {"/asdf//", "/asdf/"}, - {"/asdf/./", "/asdf/"}, - {"/a/./b", "/a/b"}, - {"/a/./b/", "/a/b/"}, - - // some slightly less easy cases - {"/asdf/..", "/"}, - {"/asdf/../", "/"}, - {"/asdf/.", "/asdf/"}, - {"/asdf//.", "/asdf/"}, - - {"/foo/bar/..", "/foo/"}, - {"/foo/bar/../baz", "/foo/baz"}, - {"/foo/bar/../baz/", "/foo/baz/"}, - {"/foo/bar/xyz/..", "/foo/bar/"}, - {"/foo/bar/xyz/../", "/foo/bar/"}, - - // going under the root or close to it - {"/..", NULL}, - {"/../asdf", NULL}, - {"/../asdf/", NULL}, - {"/./a/../..", NULL}, - {"/a/a/../..", "/"}, - {"/a/../a/..", "/"}, - {"/a/../../a", NULL}, - {"/../a/../a", NULL}, - {"/../../a/a", NULL}, - {"/////../..", NULL}, - {"//a//../..", NULL}, - - // relative paths aren't allowed - {"relative", NULL}, - {"some/stuff", NULL}, - {"./stuff", NULL}, - {"../stuff", NULL}, - {"", NULL}, - }; - - char buf[256]; - for (size_t i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) { - const char *input = testcases[i][0]; - const char *expected = testcases[i][1]; - int len = path_simplify(input, buf, strlen(input)); - if (expected == NULL) { - test(len == 0); - } else { - // TODO an argument for printing info on test failure - test(len == (int)strlen(expected) && !memcmp(expected, buf, len)); - } - } -} - -static void mount_resolve_drv(const char *path) { - if (mount_at(path) != 0) return; - - struct ufs_request res; - while (!c0_fs_wait(NULL, 0, &res)) { - // TODO does the first argument of c0_fs_respond need to be non-const? - c0_fs_respond((void*)path, strlen(path), 0); - } - exit(1); -} - -static void test_mount_resolve(void) { - const char *testcases[][2] = { - {"/", "/"}, - {"/test", "/"}, - {"/dir", "/dir"}, - {"/dir/..", "/"}, - {"/dir/../dir", "/dir"}, - {"/dirry", "/"}, - {"/dir/", "/dir"}, - {"/dir/shadowed", "/dir"}, - {"/dir/shadowed/", "/dir"}, - }; - mount_resolve_drv("/"); - mount_resolve_drv("/dir/shadowed"); - mount_resolve_drv("/dir"); - - char buf[16]; - for (size_t i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) { - const char *input = testcases[i][0]; - const char *expected = testcases[i][1]; - hid_t h = _sys_open(input, strlen(input), 0); - test(h >= 0); - int len = _sys_read(h, buf, sizeof buf, 0); - test(len == (int)strlen(expected) && !memcmp(expected, buf, len)); - _sys_close(h); - } -} - -void r_k_path(void) { - run_test(test_path_simplify); - run_test(test_mount_resolve); -} diff --git a/src/user/app/tests/kernel/threads.c b/src/user/app/tests/kernel/threads.c deleted file mode 100644 index b3c1c06..0000000 --- a/src/user/app/tests/kernel/threads.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../tests.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <string.h> -#include <esemaphore.h> -#include <thread.h> - -int global_n; -static void basic_thread(void *sem) { - global_n = 10; - esem_signal(sem); -} -static void test_basic_thread(void) { - struct evil_sem *sem = esem_new(0); - global_n = 0; - thread_create(FORK_NOREAP, basic_thread, sem); - esem_wait(sem); - test(global_n == 10); -} - -hid_t global_h; -static void shared_handle(void *sem) { - hid_t ends[2]; - test(_sys_pipe(ends, 0) >= 0); - global_h = ends[0]; - esem_signal(sem); - _sys_write(ends[1], "Hello!", 7, -1, 0); -} -static void test_shared_handle(void) { - struct evil_sem *sem = esem_new(0); - char buf[16]; - global_h = -1; - thread_create(FORK_NOREAP, shared_handle, sem); - esem_wait(sem); - - test(global_h >= 0); - test(_sys_read(global_h, buf, sizeof buf, 0) == 7); - test(!strcmp("Hello!", buf)); -} - -static void many_thread(void *arg) { - *(uint64_t*)arg += 10; -} -static void test_many_threads(void) { - uint64_t n = 0; - for (int i = 0; i < 10; i++) thread_create(0, many_thread, &n); - for (int i = 0; i < 10; i++) _sys_await(); - test(n == 100); -} - -void r_k_threads(void) { - run_test(test_basic_thread); - run_test(test_shared_handle); - run_test(test_many_threads); -} diff --git a/src/user/app/tests/libc/esemaphore.c b/src/user/app/tests/libc/esemaphore.c deleted file mode 100644 index f089f4f..0000000 --- a/src/user/app/tests/libc/esemaphore.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "../tests.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <esemaphore.h> - -static void odd(hid_t out, struct evil_sem *sem1, struct evil_sem *sem2) { - _sys_write(out, "1", 1, -1, 0); - esem_signal(sem1); - - esem_wait(sem2); - _sys_write(out, "3", 1, -1, 0); - esem_signal(sem1); - - esem_wait(sem2); - _sys_write(out, "5", 1, -1, 0); - esem_signal(sem1); -} - -static void even(hid_t out, struct evil_sem *sem1, struct evil_sem *sem2) { - esem_wait(sem1); - _sys_write(out, "2", 1, -1, 0); - esem_signal(sem2); - - esem_wait(sem1); - _sys_write(out, "4", 1, -1, 0); - esem_signal(sem2); - - esem_wait(sem1); - _sys_write(out, "6", 1, -1, 0); - esem_signal(sem2); -} - -static void test_semaphore(void) { - struct evil_sem *sem1, *sem2; - hid_t pipe[2]; - test(_sys_pipe(pipe, 0) >= 0); - - if (!fork()) { - sem1 = esem_new(0); - sem2 = esem_new(0); - test(sem1 && sem2); - if (!fork()) { - odd(pipe[1], sem1, sem2); - exit(69); - } else { - even(pipe[1], sem1, sem2); - test(_sys_await() == 69); - } - esem_free(sem1); - esem_free(sem2); - - _sys_write(pipe[1], "|", 1, -1, 0); - - sem1 = esem_new(0); - sem2 = esem_new(0); - test(sem1 && sem2); - if (!fork()) { - even(pipe[1], sem1, sem2); - exit(69); - } else { - odd(pipe[1], sem1, sem2); - test(_sys_await() == 69); - _sys_await(); - } - esem_free(sem1); - esem_free(sem2); - - _sys_filicide(); - exit(0); - } else { - close(pipe[1]); - - char buf[16]; - size_t pos = 0; - for (;;) { - int ret = _sys_read(pipe[0], buf + pos, sizeof(buf) - pos, 0); - if (ret < 0) break; - pos += ret; - } - buf[pos] = '\0'; // idc about the "potential" overflow - if (strcmp(buf, "123456|123456")) { - printf("%s\n", buf); - test_fail(); - } - - _sys_await(); - } -} - -void r_libc_esemaphore(void) { - run_test(test_semaphore); -} diff --git a/src/user/app/tests/libc/setjmp.c b/src/user/app/tests/libc/setjmp.c deleted file mode 100644 index 0dded9d..0000000 --- a/src/user/app/tests/libc/setjmp.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "../tests.h" -#include <stdbool.h> -#include <setjmp.h> - -static void test_setjmp(void) { - jmp_buf env; - volatile bool inner; - int val; - inner = false; - if (!(val = setjmp(env))) { - inner = true; - longjmp(env, 1234); - test(0); - } else { - test(val == 1234); - test(inner); - } - inner = false; - if (!(val = setjmp(env))) { - inner = true; - longjmp(env, 0); - test(0); - } else { - test(val == 1); - test(inner); - } -} - -void r_libc_setjmp(void) { - run_test(test_setjmp); -} diff --git a/src/user/app/tests/libc/string.c b/src/user/app/tests/libc/string.c deleted file mode 100644 index 6afe350..0000000 --- a/src/user/app/tests/libc/string.c +++ /dev/null @@ -1,124 +0,0 @@ -#include "../tests.h" -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> - -static void test_memcmp(void) { - test(0 == memcmp("some", "thing", 0)); - test(0 != memcmp("some", "thing", 1)); - test(0 != memcmp("some", "thing", 4)); - - test(0 == memcmp("test", "tennis", 0)); - test(0 == memcmp("test", "tennis", 1)); - test(0 == memcmp("test", "tennis", 2)); - test(0 != memcmp("test", "tennis", 3)); - test(0 != memcmp("test", "tennis", 4)); - test(0 != memcmp("test", "tennis", 5)); - - test(0 > memcmp("foo", "moo", 4)); - test(0 < memcmp("moo", "foo", 4)); - test(0 > memcmp("555", "654", 3)); - test(0 < memcmp("654", "555", 3)); -} - -static bool memall(const unsigned char *s, unsigned char c, size_t n) { - for (size_t i = 0; i < n; i++) - if (s[i] != c) return false; - return true; -} - -static void test_memset(void) { - const size_t buflen = 4096; - void *buf = malloc(buflen); - test(buf); - for (int i = 0; i < 257; i++) { - memset(buf, i, buflen); - test(memall(buf, i & 0xff, buflen)); - } - free(buf); -} - -static void test_memmove(void) { - const int partsize = 64; - char buf[partsize * 3]; - for (int i = 0; i < partsize * 2; i++) { - memset(buf, 0, sizeof buf); - for (int j = 0; j < partsize; j++) buf[i + j] = j; - memmove(buf + partsize, buf + i, partsize); - for (int j = 0; j < partsize; j++) test(buf[partsize + j] == j); - } -} - -static void test_strcmp(void) { - test(0 == strcmp("string", "string")); - test(0 > strcmp("str", "string")); - test(0 < strcmp("string", "str")); - - test(0 != strcmp("stress", "string")); - - test(0 != strncmp("abc", "ab", 3)); - test(0 == strncmp("abc", "ab", 2)); -} - -static void test_strtol(void) { - char *end; - test(1234 == strtol("1234", NULL, 10)); - test(1234 == strtol("+1234", NULL, 10)); - test(-1234 == strtol("-1234", NULL, 10)); - - test(1234 == strtol("1234", &end, 10)); - test(!strcmp("", end)); - test(1234 == strtol(" 1234hello", &end, 10)); - test(!strcmp("hello", end)); - - test(1234 == strtol(" 1234hello", &end, 0)); - test(!strcmp("hello", end)); - test(0xCAF3 == strtol(" 0xCaF3hello", &end, 0)); - test(!strcmp("hello", end)); - test(01234 == strtol(" 01234hello", &end, 0)); - test(!strcmp("hello", end)); -} - -static void test_strspn(void) { - test(0 == strspn("", "1234")); - test(0 == strspn("asdf", "1234")); - test(0 == strspn("a2df", "1234")); - test(2 == strspn("42df", "1234")); - test(4 == strspn("4211", "1234")); - - test(0 == strcspn("", "1234")); - test(4 == strcspn("asdf", "1234")); - test(1 == strcspn("a2df", "1234")); -} - -static void test_strtok(void) { - const char *sep = " \t"; - { - char line[] = "LINE TO BE SEPARATED"; - test(!strcmp(strtok(line, sep), "LINE")); - test(!strcmp(strtok(NULL, sep), "TO")); - test(!strcmp(strtok(NULL, sep), "BE")); - test(!strcmp(strtok(NULL, sep), "SEPARATED")); - for (int i = 0; i < 4; i++) - test(strtok(NULL, sep) == NULL); - } - { - char line[] = " LINE TO\tBE \t SEPARATED "; - test(!strcmp(strtok(line, sep), "LINE")); - test(!strcmp(strtok(NULL, sep), "TO")); - test(!strcmp(strtok(NULL, sep), "BE")); - test(!strcmp(strtok(NULL, sep), "SEPARATED")); - for (int i = 0; i < 4; i++) - test(strtok(NULL, sep) == NULL); - } -} - -void r_libc_string(void) { - run_test(test_memcmp); - run_test(test_memset); - run_test(test_memmove); - run_test(test_strcmp); - run_test(test_strtol); - run_test(test_strspn); - run_test(test_strtok); -} diff --git a/src/user/app/tests/shared/printf.c b/src/user/app/tests/shared/printf.c deleted file mode 100644 index d8df48a..0000000 --- a/src/user/app/tests/shared/printf.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../tests.h" -#include <stdio.h> -#include <string.h> - -#pragma GCC diagnostic ignored "-Wformat-truncation" - -static void test_printf(void) { - char buf[64]; - memset(buf, '?', 64); - - /* test proper overflow handling in snprintf */ - test(13 == snprintf(buf, 15, "That's 0x%x", 0x1337)); - test(!memcmp(buf, "That's 0x1337\0??", 16)); - test(17 == snprintf(buf, 15, "%05x %05x %05x", 0, 0, 0)); - test(!memcmp(buf, "00000 00000 00\0?", 16)); - - /* all the other stuff */ - snprintf(buf, sizeof buf, "%010x", 0x1BABE); - test(!strcmp(buf, "000001babe")); - snprintf(buf, sizeof buf, "%10x", 0x1BABE); - test(!strcmp(buf, " 1babe")); - snprintf(buf, sizeof buf, "%10s", "hello"); - test(!strcmp(buf, " hello")); - - snprintf(buf, sizeof buf, "%s%%%s", "ab", "cd"); - test(!strcmp(buf, "ab%cd")); - - snprintf(buf, sizeof buf, "%05u,%05u", 1234, 56789); - test(!strcmp(buf, "01234,56789")); - - snprintf(buf, sizeof buf, "%5d,%5d", 123, 4567); - test(!strcmp(buf, " 123, 4567")); - snprintf(buf, sizeof buf, "%5d,%5d", -123, -4567); - test(!strcmp(buf, " -123,-4567")); - - snprintf(buf, sizeof buf, "%u,%d,%x", 0, 0, 0); - test(!strcmp(buf, "0,0,0")); - - /* precision */ - snprintf(buf, sizeof buf, "%5.2u,%5.2d,%5.2x", 0, 0, 0); - test(!strcmp(buf, " 00, 00, 00")); - snprintf(buf, sizeof buf, "%5.2u,%5.2d,%5.2x", 10, -10, 0x10); - test(!strcmp(buf, " 10, -10, 10")); - snprintf(buf, sizeof buf, "%5.3d", -1); - test(!strcmp(buf, " -001")); - snprintf(buf, sizeof buf, "%.5d", 123); - test(!strcmp(buf, "00123")); - - snprintf(buf, sizeof buf, "%.1s,%.10s,%.*s", "hello", "hello", 3, "hello"); - test(!strcmp(buf, "h,hello,hel")); -} - -void r_s_printf(void) { - run_test(test_printf); -} diff --git a/src/user/app/tests/shared/ringbuf.c b/src/user/app/tests/shared/ringbuf.c deleted file mode 100644 index d2a35a1..0000000 --- a/src/user/app/tests/shared/ringbuf.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "../tests.h" -#include <shared/ring.h> -#include <string.h> - -static void test_ringbuf(void) { - char backbuf[16], cmpbuf[16]; - size_t num_read = 0, num_written = 0; - uint8_t c; - - ring_t r = {backbuf, 16, 0, 0}; - - /* aliasing */ - for (size_t i = 0; i < 16; i++) { - test(ring_used(&r) == 0); - test(ring_avail(&r) == 16); - ring_put(&r, "11 bytes...", 11); - test(ring_used(&r) == 11); - test(ring_avail(&r) == 5); - - memset(cmpbuf, 0, sizeof cmpbuf); - test(ring_get(&r, cmpbuf, 16) == 11); - test(memcmp(cmpbuf, "11 bytes...", 11) == 0); - } - - test(ring_used(&r) == 0); - for (size_t i = 0; i < 7; i++) - ring_put1b(&r, num_written++); - test(ring_used(&r) == 7); - for (size_t i = 0; i < 3; i++) { - ring_get(&r, &c, 1); - test(num_read++ == c); - } - test(ring_used(&r) == 4); - - for (size_t j = 0; j < 40; j++) { - for (size_t i = 0; i < 7; i++) - ring_put1b(&r, num_written++ & 0xff); - test(ring_used(&r) == 11); - for (size_t i = 0; i < 7; i++) { - ring_get(&r, &c, 1); - test((num_read++ & 0xff) == c); - } - test(ring_used(&r) == 4); - } -} - -void r_s_ringbuf(void) { - run_test(test_ringbuf); -} diff --git a/src/user/app/tests/stress.c b/src/user/app/tests/stress.c deleted file mode 100644 index 1ef018c..0000000 --- a/src/user/app/tests/stress.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "tests.h" -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdlib.h> -#include <unistd.h> - -static void run_forked(void (*fn)()) { - if (!fork()) { - fn(); - exit(0); - } else { - /* successful tests must return 0 - * TODO add a better fail msg */ - if (_sys_await() != 0) test_fail(); - } -} - - -static void stress_fork(void) { - for (size_t i = 0; i < 2048; i++) { - if (!fork()) exit(0); - _sys_await(); - } -} - -void stress_all(void) { - run_forked(stress_fork); -} diff --git a/src/user/app/tests/tests.c b/src/user/app/tests/tests.c deleted file mode 100644 index 5cba682..0000000 --- a/src/user/app/tests/tests.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "tests.h" -#include <camellia/syscalls.h> -#include <unistd.h> - -__attribute__((visibility("hidden"))) -extern char __executable_start[]; - -FILE *fail_trig; - -void run_test(void (*fn)()) { - if (!fork()) { - fn(); - _sys_filicide(); - exit(0); - } else { - /* successful tests must return 0 */ - if (_sys_await() != 0) { - test_failf("%p, base %p", (void*)((void*)fn - (void*)__executable_start), __executable_start); - } - } -} - -int forkpipe(FILE **f, hid_t *h) { - hid_t ends[2]; - if (_sys_pipe(ends, 0) < 0) { - fprintf(stderr, "couldn't create pipe\n"); - exit(1); - } - int ret = fork(); - if (!ret) { - close(ends[0]); - *f = fdopen(ends[1], "w"); - *h = -1; - } else { - close(ends[1]); - *f = NULL; - *h = ends[0]; - } - return ret; -} - -int main(void) { - hid_t reader; - if (!forkpipe(&fail_trig, &reader)) { - r_k_miscsyscall(); - r_k_fs(); - r_k_fdlimit(); - r_k_misc(); - r_k_path(); - r_k_threads(); - r_libc_esemaphore(); - r_libc_setjmp(); - r_libc_string(); - r_s_printf(); - r_s_ringbuf(); - exit(0); - } else { - for (;;) { - char buf[128]; - long ret = _sys_read(reader, buf, sizeof buf, 0); - if (ret < 0) break; - printf("\033[31mFAIL\033[0m "); - fwrite(buf, ret, 1, stdout); - printf("\n"); - } - } - return 0; -} diff --git a/src/user/app/tests/tests.h b/src/user/app/tests/tests.h deleted file mode 100644 index 5037e1a..0000000 --- a/src/user/app/tests/tests.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include <camellia/syscalls.h> -#include <stdio.h> -#include <stdlib.h> - -#define TMPFILEPATH "/tmp/.test_internal" - -void run_test(void (*fn)()); - -void r_k_fdlimit(void); -void r_k_fs(void); -void r_k_misc(void); -void r_k_miscsyscall(void); -void r_k_path(void); -void r_k_threads(void); -void r_libc_esemaphore(void); -void r_libc_setjmp(void); -void r_libc_string(void); -void r_s_printf(void); -void r_s_ringbuf(void); - -extern FILE *fail_trig; - -int forkpipe(FILE **f, hid_t *h); - -#define argify(str) str, sizeof(str) - 1 -#define test_fail() do { \ - fprintf(fail_trig, "%s():%u", __func__, __LINE__); \ - fflush(fail_trig); \ - exit(0); \ -} while (0) -#define test_failf(fmt, ...) do { \ - fprintf(fail_trig, "%s():%u " fmt, __func__, __LINE__, __VA_ARGS__); \ - fflush(fail_trig); \ - exit(0); \ -} while (0) -#define test(cond) if (!(cond)) test_fail(); diff --git a/src/user/app/tmpfs/tmpfs.c b/src/user/app/tmpfs/tmpfs.c deleted file mode 100644 index 6d58790..0000000 --- a/src/user/app/tmpfs/tmpfs.c +++ /dev/null @@ -1,198 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> -#include <camellia/fsutil.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -struct node { - char *name; - size_t namelen; - bool directory; - char *buf; - size_t size, capacity; - - size_t open; /* amount of open handles */ - - struct node *sibling, *child; - /* Pointer to the sibling/child field referencing the node. NULL for removed - * files. */ - struct node **ref; - - /* allocated by tmpfs_open - * freed by node_close when (open == 0 && ref == NULL && self != node_root). */ -}; - -static struct node node_root = { - .directory = true, -}; - -/** Responds to open(), finding an existing node, or creating one when applicable. */ -static struct node *tmpfs_open(const char *path, struct ufs_request *req); -/** Finds a direct child with the given name. */ -static struct node *node_getchild(struct node *parent, const char *name, size_t len); -/** Corresponds to close(); drops a reference. */ -static void node_close(struct node *node); -/** Removes a file. It's kept in memory until all the open handles are closed. */ -static long node_remove(struct node *node); - - -static struct node *node_getchild(struct node *parent, const char *name, size_t len) { - for (struct node *iter = parent->child; iter; iter = iter->sibling) { - if (iter->namelen == len && !memcmp(name, iter->name, len)) { - return iter; - } - } - return NULL; -} - -static struct node *tmpfs_open(const char *path, struct ufs_request *req) { - /* *path is not null terminated! */ - struct node *node = &node_root; - if (req->len == 0) return NULL; - if (req->len == 1) return node; /* "/" */ - path++; - req->len--; - - bool more = true; - size_t segpos = 0, seglen; /* segments end with a slash, inclusive */ - while (more) { - struct node *parent = node; - char *slash = memchr(path + segpos, '/', req->len - segpos); - seglen = (slash ? (size_t)(slash - path + 1) : req->len) - segpos; - more = segpos + seglen < req->len; - - node = node_getchild(parent, path + segpos, seglen); - if (!node) { - if (!more && (req->flags & OPEN_CREATE)) { - node = calloc(1, sizeof *node); - - node->name = malloc(seglen + 1); - if (node->name == NULL) { - free(node); - return NULL; - } - memcpy(node->name, path + segpos, seglen); - node->name[seglen] = '\0'; - - node->directory = slash; - node->namelen = seglen; - if (parent->child) { - parent->child->ref = &node->sibling; - *parent->child->ref = parent->child; - } - node->ref = &parent->child; - *node->ref = node; - } else { - return NULL; - } - } - segpos += seglen; - } - node->open++; - return node; -} - -static void node_close(struct node *node) { - node->open--; - if (!node->ref && node != &node_root && node->open == 0) { - free(node->buf); - free(node); - } -} - -static long node_remove(struct node *node) { - if (node == &node_root) return -1; - if (!node->ref) return -1; - if (node->child) return -ENOTEMPTY; - *node->ref = node->sibling; - node->ref = NULL; - node_close(node); - return 0; -} - -int main(void) { - const size_t buflen = 4096; - char *buf = malloc(buflen); - if (!buf) return -1; - - for (;;) { - struct ufs_request req; - hid_t reqh = ufs_wait(buf, buflen, &req); - struct node *ptr = req.id; - if (reqh < 0) break; - - switch (req.op) { - case VFSOP_OPEN: - ptr = tmpfs_open(buf, &req); - _sys_fs_respond(reqh, ptr, ptr ? 0 : -ENOENT, 0); - break; - - case VFSOP_READ: - if (ptr->directory) { - struct dirbuild db; - dir_start(&db, req.offset, buf, buflen); - for (struct node *iter = ptr->child; iter; iter = iter->sibling) { - dir_appendl(&db, iter->name, iter->namelen); - } - _sys_fs_respond(reqh, buf, dir_finish(&db), 0); - } else { - fs_normslice(&req.offset, &req.len, ptr->size, false); - _sys_fs_respond(reqh, ptr->buf + req.offset, req.len, 0); - } - break; - - case VFSOP_WRITE: - if (ptr->directory) { - _sys_fs_respond(reqh, NULL, -ENOSYS, 0); - break; - } - - fs_normslice(&req.offset, &req.len, ptr->size, true); - if (ptr->capacity <= req.offset + req.len) { - ptr->capacity = (req.offset + req.len + 511) & ~511; - ptr->buf = realloc(ptr->buf, ptr->capacity); - } - - memcpy(ptr->buf + req.offset, buf, req.len); - if ((req.flags & WRITE_TRUNCATE) || ptr->size < req.offset + req.len) { - ptr->size = req.offset + req.len; - } - _sys_fs_respond(reqh, NULL, req.len, 0); - break; - - case VFSOP_GETSIZE: - if (ptr->directory) { - // TODO could be cached in ptr->size - struct dirbuild db; - dir_start(&db, req.offset, NULL, buflen); - for (struct node *iter = ptr->child; iter; iter = iter->sibling) { - dir_append(&db, iter->name); - } - _sys_fs_respond(reqh, NULL, dir_finish(&db), 0); - } else { - _sys_fs_respond(reqh, NULL, ptr->size, 0); - } - break; - - case VFSOP_REMOVE: - _sys_fs_respond(reqh, NULL, node_remove(ptr), 0); - break; - - case VFSOP_CLOSE: - node_close(ptr); - _sys_fs_respond(reqh, NULL, -1, 0); - break; - - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - return 1; -} diff --git a/src/user/app/vterm/draw.c b/src/user/app/vterm/draw.c deleted file mode 100644 index ee36a0f..0000000 --- a/src/user/app/vterm/draw.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "vterm.h" -#include <camellia/execbuf.h> -#include <camellia/syscalls.h> -#include <string.h> - -struct framebuf fb; -struct rect dirty; - -void scroll(void) { - size_t row_len = fb.pitch * font.h; - memmove(fb.b, fb.b + row_len, fb.len - row_len); - memset(fb.b + fb.len - row_len, 0, row_len); - cursor.y--; - - dirty.x1 = 0; dirty.y1 = 0; - dirty.x2 = ~0; dirty.y2 = ~0; -} diff --git a/src/user/app/vterm/font.c b/src/user/app/vterm/font.c deleted file mode 100644 index 777b094..0000000 --- a/src/user/app/vterm/font.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "vterm.h" -#include <err.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -struct psf2 font; -void *font_data; - -void font_load(const char *path) { - FILE *f = fopen(path, "r"); - if (!f) { - err(1, "couldn't open font \"%s\"", path); - } - - void *buf; - long buflen; - - fseek(f, 0, SEEK_END); - buflen = ftell(f); - if (buflen < 0) { - errx(1, "can't get the size of \"%s\"", path); - } - - buf = malloc(buflen); - if (!buf) { - err(1, "out of memory"); - } - - fseek(f, 0, SEEK_SET); - fread(buf, 1, buflen, f); - if (ferror(f)) { - err(1, "error reading font"); - } - fclose(f); - - if (!memcmp(buf, "\x72\xb5\x4a\x86", 4)) { - memcpy(&font, buf, sizeof font); - font_data = buf + font.glyph_offset; - } else if (!memcmp(buf, "\x36\x04", 2)) { - struct psf1 *hdr = buf; - font = (struct psf2){ - .glyph_amt = 256, - .glyph_size = hdr->h, - .h = hdr->h, - .w = 8, - }; - font_data = buf + 4; - } else { - errx(1, "invalid psf header"); - } -} - -void font_blit(uint32_t glyph, int x, int y) { - if (glyph >= font.glyph_amt) glyph = 0; - if (x < 0 || (x+1) * font.w >= fb.width || - y < 0 || (y+1) * font.h >= fb.height) - { - return; - } - - dirty_mark(&dirty, x * font.w, y * font.h); - dirty_mark(&dirty, (x+1) * font.w - 1, (y+1) * font.h - 1); - - char *bitmap = font_data + font.glyph_size * glyph; - for (size_t i = 0; i < font.w; i++) { - for (size_t j = 0; j < font.h; j++) { - size_t idx = j * font.w + i; - char byte = bitmap[idx / 8]; - byte >>= (7-(idx&7)); - byte &= 1; - *((uint32_t*)&fb.b[fb.pitch * (y * font.h + j) + 4 * (x * font.w + i)]) = byte * 0xB0B0B0; - } - } - return; -} diff --git a/src/user/app/vterm/vterm.c b/src/user/app/vterm/vterm.c deleted file mode 100644 index f365f6b..0000000 --- a/src/user/app/vterm/vterm.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "vterm.h" -#include <camellia/syscalls.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <camellia/compat.h> - -struct point cursor = {0}; - -void in_char(char c) { - switch (c) { - case '\n': - cursor.x = 0; - cursor.y++; - break; - case '\b': - if (cursor.x > 0) cursor.x--; - break; - case '\t': - /* rounds down to nearest multiple of 8 and adds 8 - = adding 1 and rounding up to the nearest multiple of 8 */ - cursor.x = (cursor.x & ~7) + 8; - break; - default: - font_blit(c, cursor.x, cursor.y); - cursor.x++; - } - - if (cursor.x * font.w >= fb.width) { - cursor.x = 0; - cursor.y++; - } - while ((cursor.y + 1) * font.h >= fb.height) scroll(); -} - -int main(void) { - if (fb_setup(&fb, "/kdev/video/") < 0) { - eprintf("fb_setup error"); - return 1; - } - font_load("/init/usr/share/fonts/spleen/spleen-8x16.psfu"); - - static char buf[512]; - struct ufs_request res; - while (!c0_fs_wait(buf, sizeof buf, &res)) { - switch (res.op) { - case VFSOP_OPEN: - // TODO check path - c0_fs_respond(NULL, 0, 0); - break; - - case VFSOP_WRITE: - if (res.flags) { - c0_fs_respond(NULL, -1, 0); - } else { - for (size_t i = 0; i < res.len; i++) - in_char(buf[i]); - dirty_flush(&dirty, &fb); - c0_fs_respond(NULL, res.len, 0); - } - break; - - default: - c0_fs_respond(NULL, -1, 0); - break; - } - } - - return 1; -} diff --git a/src/user/app/vterm/vterm.h b/src/user/app/vterm/vterm.h deleted file mode 100644 index 026e71a..0000000 --- a/src/user/app/vterm/vterm.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include <camellia/types.h> -#include <stdint.h> -#include <stdio.h> -#include <draw.h> - -#define eprintf(fmt, ...) fprintf(stderr, "vterm: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - - -struct psf1 { - uint16_t magic; - uint8_t mode; - uint8_t h; -} __attribute__((packed)); -struct psf2 { - uint32_t magic; - uint32_t version; - uint32_t glyph_offset; - uint32_t flags; - uint32_t glyph_amt; - uint32_t glyph_size; - uint32_t h; - uint32_t w; -} __attribute__((packed)); -extern struct psf2 font; -extern void *font_data; -void font_load(const char *path); -void font_blit(uint32_t glyph, int x, int y); - -extern struct framebuf fb; - -extern struct rect dirty; -void vdirty_mark(uint32_t x, uint32_t y); -void flush(void); -void scroll(void); - -struct point {uint32_t x, y;}; -extern struct point cursor; -void in_char(char c); diff --git a/src/user/bootstrap/entry.S b/src/user/bootstrap/entry.S deleted file mode 100644 index bd1b417..0000000 --- a/src/user/bootstrap/entry.S +++ /dev/null @@ -1,27 +0,0 @@ -#define ASM_FILE 1 -#include <camellia/syscalls.h> -#include <camellia/flags.h> - -.set STACK_TOP, 0xFFFFFFFFFFFFFFFF -.set STACK_PAGES, 4 - -.section .text.startup -.global _start -.type _start, @function -_start: - mov $_SYS_MEMFLAG, %rdi - mov $(STACK_TOP & ~0xFFF - (STACK_PAGES - 1) * 0x1000), %rsi - mov $(STACK_PAGES * 0x1000), %rdx - mov $MEMFLAG_PRESENT, %r10 - syscall - - mov $_SYS_MEMFLAG, %rdi - mov $_bss_start, %rsi - mov $_bss_end, %rdx - sub $_bss_start, %rdx - mov $MEMFLAG_PRESENT, %r10 - mov %rsp, %r8 - syscall - - mov $(STACK_TOP & ~0xF), %rsp - call main // and don't you dare return diff --git a/src/user/bootstrap/linker.ld b/src/user/bootstrap/linker.ld deleted file mode 100644 index 10e3f98..0000000 --- a/src/user/bootstrap/linker.ld +++ /dev/null @@ -1,31 +0,0 @@ -ENTRY(_start) - -SECTIONS -{ - . = 0x20000; - _bss_start = .; - .bss BLOCK(4K) : ALIGN(4K) - { - *(COMMON) - *(.bss) - } - _bss_end = .; - - . = 2M; - .text BLOCK(4K) : ALIGN(4K) - { - /* the assert needs to be inside of a section or it'll be a syntax error */ - ASSERT(_bss_end <= 2M, "bss too big, needs to be moved down"); - *(.text.startup) - *(.text) - } - .rodata BLOCK(4K) : ALIGN(4K) - { - *(.rodata) - } - .data BLOCK(4K) : ALIGN(4K) - { - *(.data*) - } - _initrd = .; /* is just appended onto the end of the binary */ -} diff --git a/src/user/bootstrap/main.c b/src/user/bootstrap/main.c deleted file mode 100644 index d27da39..0000000 --- a/src/user/bootstrap/main.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <_proc.h> -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdio.h> -#include <string.h> -#include <elfload.h> -#include <camellia/fs/misc.h> - -#include "tar.h" - -extern char _bss_start; -extern char _bss_end; -extern char _initrd; - -__attribute__((section(".text"))) -int main(void) { - _sys_memflag(_psdata_loc, 1, MEMFLAG_PRESENT); - setprogname("bootstrap"); - - _sys_mount(HANDLE_PROCFS, "/proc/", strlen("/proc/")); - MOUNT_AT("/") { - fs_dirinject2((const char*[]) { - "/proc/", - "/init/", - NULL - }); - } - MOUNT_AT("/init/") { - tar_driver(&_initrd); - } - - const char *initpath = "bin/amd64/init"; - char *initargv[] = {"init", NULL}; - void *init = tar_find(initpath, strlen(initpath), &_initrd, ~0) + 512; - if (init) { - _klogf("execing init"); - elf_exec(init, initargv, NULL); - _klogf("elf_exec failed"); - } else { - _klogf("couldn't find init.elf"); - } - _sys_exit(1); -} diff --git a/src/user/bootstrap/tar.c b/src/user/bootstrap/tar.c deleted file mode 100644 index 2020fe0..0000000 --- a/src/user/bootstrap/tar.c +++ /dev/null @@ -1,155 +0,0 @@ -#include "tar.h" -#include <camellia/flags.h> -#include <camellia/fsutil.h> -#include <camellia/syscalls.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <camellia/compat.h> -#include <camellia/fs/dir.h> - -#define BUF_SIZE 64 - -static void *tar_open(const char *path, int len, void *base, size_t base_len); -static char tar_type(void *meta); -static void tar_dirbuild(struct dirbuild *db, const char *meta, void *base, size_t base_len); -static void tar_read(struct ufs_request *res, void *base, size_t base_len); -static int tar_size(void *sector); -static int oct_parse(char *str, size_t len); - - -static const char *root_fakemeta = ""; /* see comment in tar_open */ - - -void tar_driver(void *base) { - static char buf[BUF_SIZE]; - struct ufs_request res; - void *ptr; - while (!c0_fs_wait(buf, BUF_SIZE, &res)) { - switch (res.op) { - case VFSOP_OPEN: - ptr = tar_open(buf, res.len, base, ~0); - c0_fs_respond(ptr, ptr ? 0 : -1, 0); - break; - - case VFSOP_READ: - tar_read(&res, base, ~0); - break; - - case VFSOP_GETSIZE: - if (tar_type(res.id) != '5') { - c0_fs_respond(NULL, tar_size(res.id), 0); - } else { - struct dirbuild db; - dir_start(&db, res.offset, NULL, 0); - tar_dirbuild(&db, res.id, base, ~0); - c0_fs_respond(NULL, dir_finish(&db), 0); - } - break; - - default: - c0_fs_respond(NULL, -1, 0); // unsupported - break; - } - } - exit(0); -} - -static char tar_type(void *meta) { - if (meta == root_fakemeta) return '5'; - return *(char*)(meta + 156); -} - -static void *tar_open(const char *path, int len, void *base, size_t base_len) { - if (len <= 0) return NULL; - path += 1; // skip the leading slash - len -= 1; - - /* TAR archives don't (seem to) contain an entry for the root dir, so i'm - * returning a fake one. this isn't a full entry because i'm currently too - * lazy to create a full one - thus, it has to be special cased in tar_read */ - if (len == 0) - return (void*)root_fakemeta; - - return tar_find(path, len, base, base_len); -} - -static void tar_dirbuild(struct dirbuild *db, const char *meta, void *base, size_t base_len) { - size_t meta_len = strlen(meta); - for (size_t off = 0; off < base_len;) { - if (0 != memcmp(base + off + 257, "ustar", 5)) - break; // not a metadata sector - - /* check if prefix matches */ - if (0 == memcmp(base + off, meta, meta_len) - && *(char*)(base + off + meta_len) != '\0') { - char *suffix = base + off + meta_len; - - /* check if the path contains any non-trailing slashes */ - char *slash = strchr(suffix, '/'); - if (!slash || slash[1] == '\0') { - if (dir_append(db, suffix)) break; - } - } - - int size = tar_size(base + off); - off += 512; // skip this metadata sector - off += (size + 511) & ~511; // skip the data sectors - } -} - -static void tar_read(struct ufs_request *res, void *base, size_t base_len) { - void *meta = (void*)res->id; - static char buf[BUF_SIZE]; - // TODO reuse a single buffer for both tar_driver and tar_read - - switch (tar_type(meta)) { - case '\0': - case '0': /* normal files */ - fs_normslice(&res->offset, &res->len, tar_size(meta), false); - c0_fs_respond(meta + 512 + res->offset, res->len, 0); - break; - - case '5': /* directory */ - struct dirbuild db; - dir_start(&db, res->offset, buf, sizeof buf); - tar_dirbuild(&db, meta, base, base_len); - c0_fs_respond(buf, dir_finish(&db), 0); - break; - - default: - c0_fs_respond(NULL, -1, 0); - break; - } -} - -static int tar_size(void *sector) { - return oct_parse(sector + 124, 11); -} - -void *tar_find(const char *path, size_t path_len, void *base, size_t base_len) { - int size; - if (path_len > 100) return NULL; // illegal path - - for (size_t off = 0; off < base_len;) { - if (0 != memcmp(base + off + 257, "ustar", 5)) - break; // not a metadata sector - if (0 == memcmp(base + off, path, path_len) && - *(char*)(base + off + path_len) == '\0') - return base + off; // file found, quit - - size = tar_size(base + off); - off += 512; // skip this metadata sector - off += (size + 511) & ~511; // skip the data sectors - } - return NULL; -} - -static int oct_parse(char *str, size_t len) { - int res = 0; - for (size_t i = 0; i < len; i++) { - res *= 8; - res += str[i] - '0'; // no format checking - } - return res; -} diff --git a/src/user/bootstrap/tar.h b/src/user/bootstrap/tar.h deleted file mode 100644 index e7ab130..0000000 --- a/src/user/bootstrap/tar.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include <camellia/types.h> -#include <stddef.h> - -_Noreturn void tar_driver(void *base); -void *tar_find(const char *path, size_t path_len, void *base, size_t base_len); diff --git a/src/user/lib/_start.s b/src/user/lib/_start.s deleted file mode 100644 index 12d2c5f..0000000 --- a/src/user/lib/_start.s +++ /dev/null @@ -1,17 +0,0 @@ -.section .text -.global _start -.type _start, @function -.weak _start -_start: - mov %rsp, %rdi - and $~0xF, %rsp - - /* prevent floating point crashes. thanks heat */ - push $0x1f80 - ldmxcsr (%rsp) - add $8, %rsp - - call _start2 - hlt - /* the call shouldn't return, thus the hlt. - * using a call instead of jmp for stack alignment */ diff --git a/src/user/lib/_start2.c b/src/user/lib/_start2.c deleted file mode 100644 index b4eb76a..0000000 --- a/src/user/lib/_start2.c +++ /dev/null @@ -1,42 +0,0 @@ -#include <_proc.h> -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <elfload.h> - -int main(int argc, char **argv, char **envp); - -__attribute__((visibility("hidden"))) -extern char __executable_start[]; - -const char *shortname(const char *path) { - if (!path) return "unknown program"; - const char *slash = strrchr(path, '/'); - if (slash) return slash + 1; - return path; -} - -void intr_trampoline(void); /* intr.s */ - -_Noreturn void _start2(struct execdata *ed) { - const char *progname; - elf_selfreloc(); - - /* done first so it isn't allocated elsewhere by accident */ - _sys_memflag(_psdata_loc, 1, MEMFLAG_PRESENT); - _psdata_loc->base = __executable_start; - /* sets ->desc */ - progname = shortname(ed->argv[0]); - setprogname(progname); - - _klogf("_start2 %s %p", progname, __executable_start); - - _sys_intr_set(intr_trampoline); - intr_set(intr_default); - __setinitialcwd(ed->cwd); - - exit(main(ed->argc, ed->argv, ed->envp)); -} diff --git a/src/user/lib/assert.c b/src/user/lib/assert.c deleted file mode 100644 index 0c46450..0000000 --- a/src/user/lib/assert.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <assert.h> -#include <camellia/syscalls.h> -#include <stdio.h> - -_Noreturn void __badassert(const char *func, const char *file, int line) { - fprintf(stderr, "assertion failure %s:%s:%u\n", file, func, line); - _sys_exit(1); -} diff --git a/src/user/lib/camellia.c b/src/user/lib/camellia.c deleted file mode 100644 index 4e092e4..0000000 --- a/src/user/lib/camellia.c +++ /dev/null @@ -1,30 +0,0 @@ -#include <camellia.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -hid_t camellia_open(const char *path, int flags) { - hid_t ret; - char *buf; - size_t len; - - if (path == NULL) - return errno = EINVAL, -EINVAL; - if (flags & OPEN_CREATE) - flags |= OPEN_WRITE; - - len = absolutepath(NULL, path, 0); - buf = malloc(len); - if (!buf) - return -errno; - absolutepath(buf, path, len); - ret = _sys_open(buf, strlen(buf), flags); - free(buf); - - if (ret < 0) - errno = -ret; - - return ret; -} diff --git a/src/user/lib/compat.c b/src/user/lib/compat.c deleted file mode 100644 index 3ec47f9..0000000 --- a/src/user/lib/compat.c +++ /dev/null @@ -1,20 +0,0 @@ -#include <camellia/syscalls.h> -#include <stdio.h> -#include <camellia/compat.h> - -#define eprintf(fmt, ...) fprintf(stderr, "user/lib/compat: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - -static hid_t h = -1; -long c0_fs_wait(char *buf, long len, struct ufs_request *res) { - if (h != -1) { - eprintf("didn't respond to request!"); - c0_fs_respond(NULL, -1, 0); - } - h = _sys_fs_wait(buf, len, res); - return h >= 0 ? 0 : -1; -} -long c0_fs_respond(void *buf, long ret, int flags) { - ret = _sys_fs_respond(h, buf, ret, flags); - h = -1; - return ret; -} diff --git a/src/user/lib/ctype.c b/src/user/lib/ctype.c deleted file mode 100644 index fa49a07..0000000 --- a/src/user/lib/ctype.c +++ /dev/null @@ -1,65 +0,0 @@ -#include <ctype.h> - -int isalnum(int c) { - return isalpha(c) || isdigit(c); -} - -int isalpha(int c) { - return islower(c) || isupper(c); -} - -int iscntrl(int c) { - return c <= 0x1f || c == 0x7f; -} - -int isdigit(int c) { - return '0' <= c && c <= '9'; -} - -int isgraph(int c) { - return isalpha(c) || isdigit(c) || ispunct(c); -} - -int islower(int c) { - return 'a' <= c && c <= 'z'; -} - -int isprint(int c) { - return isgraph(c) || c == ' '; -} - -int ispunct(int c) { - return ('!' <= c && c <= '/') - || (':' <= c && c <= '@') - || ('[' <= c && c <= '`') - || ('{' <= c && c <= '~'); -} - -int isspace(int c) { - return c == ' ' - || c == '\f' - || c == '\n' - || c == '\r' - || c == '\t' - || c == '\v'; -} - -int isupper(int c) { - return 'A' <= c && c <= 'Z'; -} - -int isxdigit(int c) { - return ('0' <= c && c <= '9') - || ('A' <= c && c <= 'F') - || ('a' <= c && c <= 'f'); -} - -int tolower(int c) { - if (isupper(c)) return c - 'A' + 'a'; - return c; -} - -int toupper(int c) { - if (islower(c)) return c - 'a' + 'A'; - return c; -} diff --git a/src/user/lib/dirent.c b/src/user/lib/dirent.c deleted file mode 100644 index c2d1b9c..0000000 --- a/src/user/lib/dirent.c +++ /dev/null @@ -1,55 +0,0 @@ -#include <dirent.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> - -DIR *opendir(const char *name) { - FILE *fp = NULL; - DIR *dir = NULL; - fp = fopen(name, "r"); - if (!fp) { - goto err; - } - dir = calloc(1, sizeof *dir); - if (!dir) { - goto err; - } - dir->fp = fp; - return dir; -err: - if (fp) fclose(fp); - free(dir); - return NULL; -} - -int closedir(DIR *dir) { - fclose(dir->fp); - free(dir); - return 0; -} - -struct dirent *readdir(DIR *dir) { - int i = 0; - char *buf = dir->dent.d_name; - for (;;) { - int c = fgetc(dir->fp); - if (c == EOF) { - if (i == 0) return NULL; - else break; - } - if (c == '\0') { - break; - } - if (i == sizeof(dir->dent.d_name)-1) { - /* overflow */ - for (;;) { - c = fgetc(dir->fp); - if (c == EOF || c == '\0') break; - } - return errno = ENAMETOOLONG, NULL; - } - buf[i++] = c; - } - buf[i] = '\0'; - return &dir->dent; -} diff --git a/src/user/lib/draw/draw.c b/src/user/lib/draw/draw.c deleted file mode 100644 index 1c2a371..0000000 --- a/src/user/lib/draw/draw.c +++ /dev/null @@ -1,83 +0,0 @@ -#include <camellia.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <draw.h> - -void dirty_reset(struct rect *d) { - d->x1 = ~0; d->y1 = ~0; - d->x2 = 0; d->y2 = 0; -} - -void dirty_mark(struct rect *d, uint32_t x, uint32_t y) { - if (d->x1 > x) d->x1 = x; - if (d->x2 < x) d->x2 = x; - if (d->y1 > y) d->y1 = y; - if (d->y2 < y) d->y2 = y; -} - -int fb_setup(struct framebuf *fb, const char *base) { - char path[64], *spec; - size_t pos; - FILE *f; - - f = fopen(base, "r"); - if (!f) return -errno; - - pos = strlen(base); - memcpy(path, base, pos); - spec = path + pos; - fread(spec, 1, sizeof(path) - pos, f); - /* assumes the read went correctly */ - fclose(f); - - fb->fd = camellia_open(path, OPEN_RW); - if (fb->fd < 0) return fb->fd; - - fb->width = strtol(spec, &spec, 0); - if (*spec++ != 'x') return -EINVAL; - fb->height = strtol(spec, &spec, 0); - if (*spec++ != 'x') return -EINVAL; - fb->bpp = strtol(spec, &spec, 0); - if (fb->bpp != 32) return -EINVAL; - - fb->len = _sys_getsize(fb->fd); - fb->pitch = fb->len / fb->height; - fb->b = malloc(fb->len); - - _sys_read(fb->fd, fb->b, fb->len, 0); - - return 0; -} - -int fb_anon(struct framebuf *fb, size_t w, size_t h) { - fb->width = w; - fb->height = h; - fb->bpp = 32; - fb->pitch = fb->width * fb->bpp / 8; - fb->len = fb->pitch * fb->height; - fb->b = malloc(fb->len); - fb->fd = -1; - return 0; -} - -uint32_t *fb_pixel(struct framebuf *fb, uint32_t x, uint32_t y) { - if (x < fb->width && y < fb->height) - return (void*)fb->b + fb->pitch * y + 4 * x; - return NULL; -} - -void fb_cpy( - struct framebuf *dest, const struct framebuf *src, - size_t xd, size_t yd, size_t xs, size_t ys, size_t w, size_t h) -{ - for (size_t y = 0; y < h; y++) { - memcpy( - fb_pixel( dest, xd, yd + y), - fb_pixel((void*)src, xs, ys + y), - w * 4 - ); - } -} diff --git a/src/user/lib/draw/flush.c b/src/user/lib/draw/flush.c deleted file mode 100644 index 88bf3d6..0000000 --- a/src/user/lib/draw/flush.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <camellia/execbuf.h> -#include <camellia/syscalls.h> -#include <draw.h> - -static void flush_combined(struct rect pix, struct framebuf *fb) { - size_t low = fb->pitch * pix.y1 + 4 * pix.x1; - size_t high = fb->pitch * pix.y2 + 4 * pix.y2 + 4; - _sys_write(fb->fd, fb->b + low, high - low, low, 0); -} - -static void flush_split(struct rect pix, struct framebuf *fb) { - static uint64_t execbuf[EXECBUF_MAX_LEN / sizeof(uint64_t)]; - size_t epos = 0; - if (7 * (pix.y2 - pix.y1) * sizeof(uint64_t) >= sizeof execbuf) { - flush_combined(pix, fb); - return; - } - - for (uint32_t y = pix.y1; y < pix.y2; y++) { - size_t low = fb->pitch * y + 4 * pix.x1; - size_t high = fb->pitch * y + 4 * pix.x2 + 4; - - execbuf[epos++] = EXECBUF_SYSCALL; - execbuf[epos++] = _SYS_WRITE; - execbuf[epos++] = fb->fd; - execbuf[epos++] = (uintptr_t)fb->b + low; - execbuf[epos++] = high - low; - execbuf[epos++] = low; - execbuf[epos++] = 0; - } - _sys_execbuf(execbuf, epos * sizeof(uint64_t)); -} - -void dirty_flush(struct rect *d, struct framebuf *fb) { - if (~d->x1 == 0) return; - if (d->x2 >= fb->width) d->x2 = fb->width - 1; - if (d->y2 >= fb->height) d->y2 = fb->height - 1; - - /* the threshold is mostly arbitrary, wasn't based on any real benchmarks */ - if (d->x2 - d->x1 > fb->width - 600) flush_combined(*d, fb); - else flush_split(*d, fb); - dirty_reset(d); -} diff --git a/src/user/lib/elf.h b/src/user/lib/elf.h deleted file mode 100644 index 8dc6242..0000000 --- a/src/user/lib/elf.h +++ /dev/null @@ -1,188 +0,0 @@ -/* shamelessly stolen from https://github.com/adachristine/sophia/tree/main/api/elf */ -#pragma once -#include <stdint.h> - -typedef uint8_t Elf_Byte; - -#define EI_MAG0 0 -#define EI_MAG1 1 -#define EI_MAG2 2 -#define EI_MAG3 3 -#define EI_CLASS 4 -#define EI_DATA 5 -#define EI_VERSION 6 -#define EI_OSABI 7 -#define EI_ABIVERSION 8 -#define EI_PAD 9 -#define EI_NIDENT 16 - -#define ELFMAG0 0x7f -#define ELFMAG1 'E' -#define ELFMAG2 'L' -#define ELFMAG3 'F' - -#define ELFCLASSNONE 0 -#define ELFCLASS32 1 -#define ELFCLASS64 2 - -#define ELFDATANONE 0 -#define ELFDATA2LSB 1 -#define ELFDATA2MSB 2 - -#define EI_VERSION 6 -#define EV_NONE 0 -#define EV_CURRENT 1 - -#define ELFOSABI_NONE 0 -#define ELFOSABI_SYSV EI_OSABI_NONE - -#define ET_NONE 0 -#define ET_REL 1 -#define ET_EXEC 2 -#define ET_DYN 3 -#define ET_CORE 4 -#define ET_LOOS 0xfe00 -#define ET_HIOS 0xfeff - -#define EM_NONE 0 - -#define PT_NULL 0 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 - -#define PF_X 0x01 -#define PF_W 0x02 -#define PF_R 0x04 - -#define SHT_NULL 0 -#define SHT_PROGITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 - -#define SHF_WRITE 0x1 -#define SHF_ALLOC 0x2 -#define SHF_EXEC 0x4 -#define SHF_MERGE 0x10 -#define SHF_STRINGS 0x20 - -#define DT_NULL 0 -#define DT_NEEDED 1 -#define DT_PLTRELSZ 2 -#define DT_PLTGOT 3 -#define DT_HASH 4 -#define DT_STRTAB 5 -#define DT_SYMTAB 6 -#define DT_RELA 7 -#define DT_RELASZ 8 -#define DT_RELAENT 9 -#define DT_STRSZ 10 -#define DT_SYMENT 11 -#define DT_JMPREL 0x17 - -typedef uint64_t Elf64_Addr; -typedef uint64_t Elf64_Off; -typedef uint16_t Elf64_Section; -typedef uint16_t Elf64_Versym; -typedef uint16_t Elf64_Half; -typedef int32_t Elf64_Sword; -typedef uint32_t Elf64_Word; -typedef int64_t Elf64_Sxword; -typedef uint64_t Elf64_Xword; - -typedef struct Elf64_Ehdr Elf64_Ehdr; -typedef struct Elf64_Phdr Elf64_Phdr; -typedef struct Elf64_Shdr Elf64_Shdr; -typedef struct Elf64_Sym Elf64_Sym; -typedef struct Elf64_Dyn Elf64_Dyn; - -typedef struct Elf64_Rel Elf64_Rel; -typedef struct Elf64_Rela Elf64_Rela; - -#define EM_X86_64 62 - -struct Elf64_Ehdr -{ - Elf_Byte e_ident[EI_NIDENT]; - Elf64_Half e_type; - Elf64_Half e_machine; - Elf64_Word e_version; - Elf64_Addr e_entry; - Elf64_Off e_phoff; - Elf64_Off e_shoff; - Elf64_Word e_flags; - Elf64_Half e_ehsize; - Elf64_Half e_phentsize; - Elf64_Half e_phnum; - Elf64_Half e_shentsize; - Elf64_Half e_shnum; - Elf64_Half e_shstrndx; -}; - -struct Elf64_Phdr -{ - Elf64_Word p_type; - Elf64_Word p_flags; - Elf64_Off p_offset; - Elf64_Addr p_vaddr; - Elf64_Addr p_paddr; - Elf64_Xword p_filesz; - Elf64_Xword p_memsz; - Elf64_Xword p_align; -}; - -struct Elf64_Shdr -{ - Elf64_Word sh_name; - Elf64_Word sh_type; - Elf64_Xword sh_flags; - Elf64_Addr sh_addr; - Elf64_Off sh_offset; - Elf64_Xword sh_size; - Elf64_Word sh_link; - Elf64_Word sh_info; - Elf64_Off sh_addralign; - Elf64_Xword sh_entsize; -}; - -struct Elf64_Sym -{ - Elf64_Word st_name; - Elf_Byte st_info; - Elf_Byte st_other; - Elf64_Half st_shndx; - Elf64_Addr st_value; - Elf64_Xword st_size; -}; - -struct Elf64_Dyn -{ - Elf64_Xword d_tag; - union - { - Elf64_Xword d_val; - Elf64_Addr d_ptr; - }; -}; - -#define ELF64_R_SYM(info) ((info)>>32) -#define ELF64_R_TYPE(info) ((Elf64_Word)(info)) -#define ELF64_R_INFO(sym, type) (((Elf64_Xword)(sym)<<32)+(Elf64_Xword)(type)) - -#define R_X86_64_JUMP_SLOT 7 -#define R_X86_64_RELATIVE 8 - -struct Elf64_Rel -{ - Elf64_Addr r_offset; - Elf64_Xword r_info; -}; - -struct Elf64_Rela -{ - Elf64_Addr r_offset; - Elf64_Xword r_info; - Elf64_Sxword r_addend; -}; diff --git a/src/user/lib/elfload.S b/src/user/lib/elfload.S deleted file mode 100644 index 78d5b3c..0000000 --- a/src/user/lib/elfload.S +++ /dev/null @@ -1,20 +0,0 @@ -#define ASM_FILE 1 -#include <camellia/syscalls.h> - -.section .text -.global _freejmp_chstack -.type _freejmp_chstack, @function -// void _freejmp_chstack(void *entry, void *low, size_t len, char **argv, char **envp, void *stack); -_freejmp_chstack: - mov %r9, %rsp - jmp _freejmp - -.section .text -.global execbuf_chstack -.type execbuf_chstack, @function -// _Noreturn void execbuf_chstack(void *stack, void __user *buf, size_t len); -execbuf_chstack: - mov %rdi, %rsp - mov $_SYS_EXECBUF, %rdi - syscall - hlt // if execbuf failed we might as well crash diff --git a/src/user/lib/elfload.c b/src/user/lib/elfload.c deleted file mode 100644 index c1a08f8..0000000 --- a/src/user/lib/elfload.c +++ /dev/null @@ -1,182 +0,0 @@ -#include <camellia/execbuf.h> -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include "elf.h" -#include <elfload.h> - -void elf_execf(FILE *f, char **argv, char **envp) { - size_t ret; - void *buf; - long buflen; - - fseek(f, 0, SEEK_END); - buflen = ftell(f); - if (buflen < 0) return; /* errno set by fseek */ - buf = malloc(buflen); - - // TODO don't read the entire file into memory - fseek(f, 0, SEEK_SET); - if (!buf) return; - ret = fread(buf, buflen, 1, f); - fclose(f); - if (ret == 1) { - elf_exec(buf, argv, envp); - } - free(buf); -} - -static bool valid_ehdr(const struct Elf64_Ehdr *h) { - return h->e_ident[0] == 0x7f - && h->e_ident[1] == 'E' - && h->e_ident[2] == 'L' - && h->e_ident[3] == 'F' - && h->e_machine == EM_X86_64 - && h->e_version == 1; -} - -static bool load_phdr(const void *elf, void *exebase, size_t idx) { - const struct Elf64_Ehdr *ehdr = elf; - const struct Elf64_Phdr *phdr = elf + ehdr->e_phoff + idx * ehdr->e_phentsize; - - if (phdr->p_type == PT_DYNAMIC) return true; - - if (phdr->p_type != PT_LOAD) { - printf("unknown type %x\n", phdr->p_type); - return false; - } - // TODO overlap check - // TODO don't ignore flags - _sys_memflag(exebase + phdr->p_vaddr, phdr->p_memsz, MEMFLAG_PRESENT); - // TODO check that filesz <= memsz - memcpy(exebase + phdr->p_vaddr, elf + phdr->p_offset, phdr->p_filesz); - return true; -} - -static size_t elf_spread(const void *elf) { - const struct Elf64_Ehdr *ehdr = elf; - uintptr_t high = 0, low = ~0; - for (size_t phi = 0; phi < ehdr->e_phnum; phi++) { - const struct Elf64_Phdr *phdr = elf + ehdr->e_phoff + phi * ehdr->e_phentsize; - if (high < phdr->p_vaddr + phdr->p_memsz) - high = phdr->p_vaddr + phdr->p_memsz; - if (low > phdr->p_vaddr) - low = phdr->p_vaddr; - } - return high - low; -} - -static void *memdup(const void *orig, size_t len) { - void *n = malloc(len); - if (n) memcpy(n, orig, len); - return n; -} - -static const char *default_argv[] = {NULL}; - -/* frees memory outside of [low; low + len] and jumps to *entry - * also sets up main's stack */ -void _freejmp_chstack(void *entry, void *low, size_t len, const char **argv, char **envp, void *stack); // elfload.s -_Noreturn void execbuf_chstack(void *stack, void __user *buf, size_t len); -void _freejmp(void *entry, void *low, size_t imglen, const char **argv, char **envp) { - // TODO error checking - // although i guess it's fine for it to crash - // also TODO just alloc a new stack - void *stack = (void*)~0; - struct execdata ed; - size_t argv_len; - size_t cwd_len = absolutepath(NULL, NULL, 0); - - if (!argv) argv = default_argv; - - ed.argc = 0; - while (argv[ed.argc]) ed.argc++; - argv_len = (ed.argc+1) * sizeof(char *); - - /* make a copy of argv, so it doesn't get overridden - * if it overlaps with the new stack. */ - argv = memdup(argv, argv_len); - for (int i = 0; i < ed.argc; i++) { - argv[i] = strdup(argv[i]); - } - ed.cwd = malloc(cwd_len); - getcwd(ed.cwd, cwd_len); - - stack -= argv_len; - ed.argv = stack; - - for (int i = 0; i < ed.argc; i++) { - size_t len = strlen(argv[i]) + 1; - stack -= len; - memcpy(stack, argv[i], len); - ed.argv[i] = stack; - } - ed.argv[ed.argc] = NULL; - - /* push cwd */ - stack -= cwd_len; - memcpy(stack, ed.cwd, cwd_len); - ed.cwd = stack; - - stack -= sizeof ed; - memcpy(stack, &ed, sizeof ed); - - uintptr_t high = (uintptr_t)low + imglen; - uint64_t buf[] = { - EXECBUF_SYSCALL, _SYS_MEMFLAG, 0, (uintptr_t)low, 0, 0, 0, - EXECBUF_SYSCALL, _SYS_MEMFLAG, high, ~0 - 0xF000 - high, 0, 0, 0, - EXECBUF_JMP, (uintptr_t)entry, - }; - execbuf_chstack(stack, buf, sizeof buf); -} - -static void *elf_loadmem(struct Elf64_Ehdr *ehdr) { - void *exebase; - size_t spread = elf_spread(ehdr); - switch (ehdr->e_type) { - case ET_EXEC: - exebase = (void*)0; - break; - case ET_DYN: - exebase = _sys_memflag((void*)0x1000, spread, MEMFLAG_FINDFREE); - if (!exebase) - return NULL; - break; - default: - return NULL; - } - for (size_t phi = 0; phi < ehdr->e_phnum; phi++) { - if (!load_phdr((void*)ehdr, exebase, phi)) { - _sys_memflag(exebase, spread, 0); - return NULL; - } - } - return exebase; -} - -void elf_exec(void *base, char **argv, char **envp) { - struct Elf64_Ehdr *ehdr = base; - if (!valid_ehdr(ehdr)) return; - - void *exebase = elf_loadmem(ehdr); - if (!exebase) return; - - void *newstack = _sys_memflag((void*)0x11000, 0x1000, MEMFLAG_FINDFREE | MEMFLAG_PRESENT) + 0x1000 - 8; - if (!newstack) return; - - _freejmp_chstack(exebase + ehdr->e_entry, exebase, elf_spread(ehdr) + 0x1000, (const char**)argv, envp, newstack); -} - -void *elf_partialexec(void *base) { - struct Elf64_Ehdr *ehdr = base; - if (!valid_ehdr(ehdr)) return NULL; - - void *exebase = elf_loadmem(ehdr); - if (!exebase) return NULL; - - return exebase + ehdr->e_entry; -} diff --git a/src/user/lib/elfreloc.c b/src/user/lib/elfreloc.c deleted file mode 100644 index aab2a2a..0000000 --- a/src/user/lib/elfreloc.c +++ /dev/null @@ -1,39 +0,0 @@ -#include <stdio.h> -#include "elf.h" - -__attribute__((visibility("hidden"))) -extern struct Elf64_Dyn _DYNAMIC[]; - -__attribute__((visibility("hidden"))) -extern char __executable_start[]; - -static struct Elf64_Dyn *dyn_gettag(Elf64_Xword tag) { - for (size_t i = 0;; i++) { - if (_DYNAMIC[i].d_tag == tag) return &_DYNAMIC[i]; - if (_DYNAMIC[i].d_tag == DT_NULL) return NULL; - } -} - -void elf_selfreloc(void) { - // TODO DT_REL, DT_JMPREL - - struct Elf64_Dyn *rela_tag = dyn_gettag(DT_RELA); - if (rela_tag) { - /* not checking pointer validity, - * crashing on an invalid elf is fine */ - size_t relasz = dyn_gettag(DT_RELASZ)->d_val; - size_t relaent = dyn_gettag(DT_RELAENT)->d_val; - for (size_t o = 0; o < relasz; o += relaent) { - struct Elf64_Rela *r = (void*)__executable_start + rela_tag->d_ptr + o; - uintptr_t *target = (void*)__executable_start + r->r_offset; - - switch (ELF64_R_TYPE(r->r_info)) { - case R_X86_64_RELATIVE: - *target = (uintptr_t)&__executable_start + r->r_addend; - break; - default: - _klogf("elf: unsupported relocation type\n"); - } - } - } -} diff --git a/src/user/lib/err.c b/src/user/lib/err.c deleted file mode 100644 index 7a220fe..0000000 --- a/src/user/lib/err.c +++ /dev/null @@ -1,50 +0,0 @@ -#include <err.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -_Noreturn void err(int ret, const char *fmt, ...) { - va_list args; - va_start(args, fmt); - vwarn(fmt, args); - va_end(args); - exit(ret); -} - -_Noreturn void errx(int ret, const char *fmt, ...) { - va_list args; - va_start(args, fmt); - vwarnx(fmt, args); - va_end(args); - exit(ret); -} - -void warn(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - vwarn(fmt, args); - va_end(args); -} - -void warnx(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - vwarnx(fmt, args); - va_end(args); -} - -void vwarn(const char *fmt, va_list args) { - fprintf(stderr, "%s: ", getprogname()); - if (fmt) { - vfprintf(stderr, fmt, args); - fprintf(stderr, ": "); - } - fprintf(stderr, "%s\n", strerror(errno)); -} - -void vwarnx(const char *fmt, va_list args) { - fprintf(stderr, "%s: ", getprogname()); - if (fmt) vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); -} diff --git a/src/user/lib/esemaphore.c b/src/user/lib/esemaphore.c deleted file mode 100644 index 2707d11..0000000 --- a/src/user/lib/esemaphore.c +++ /dev/null @@ -1,56 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <stdlib.h> -#include <unistd.h> -#include <esemaphore.h> - -void esem_signal(struct evil_sem *sem) { - _sys_write(sem->signal, NULL, 0, 0, 0); -} - -void esem_wait(struct evil_sem *sem) { - _sys_read(sem->wait, NULL, 0, 0); -} - -struct evil_sem *esem_new(int value) { - hid_t ends_wait[2], ends_signal[2]; - struct evil_sem *sem; - - if (value < 0) return NULL; - if (_sys_pipe(ends_wait, 0) < 0) return NULL; - if (_sys_pipe(ends_signal, 0) < 0) goto fail_signal; - if (!(sem = malloc(sizeof *sem))) goto fail_malloc; - - if (!_sys_fork(FORK_NOREAP, NULL)) { - close(ends_signal[1]); - while (_sys_read(ends_signal[0], NULL, 0, 0) >= 0) { - if (!_sys_fork(FORK_NOREAP, NULL)) { - _sys_write(ends_wait[1], NULL, 0, 0, 0); - exit(0); - } - } - exit(0); - } - close(ends_signal[0]); - close(ends_wait[1]); - - sem->wait = ends_wait[0]; - sem->signal = ends_signal[1]; - - while (value--) esem_signal(sem); - return sem; - -fail_malloc: - close(ends_signal[0]); - close(ends_signal[1]); -fail_signal: - close(ends_wait[0]); - close(ends_wait[1]); - return NULL; -} - -void esem_free(struct evil_sem *sem) { - close(sem->wait); - close(sem->signal); - free(sem); -} diff --git a/src/user/lib/fcntl.c b/src/user/lib/fcntl.c deleted file mode 100644 index b2d8685..0000000 --- a/src/user/lib/fcntl.c +++ /dev/null @@ -1,28 +0,0 @@ -#include <bits/panic.h> -#include <camellia.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> - -#include <stdio.h> - -int open(const char *path, int flags, ...) { - (void)path; (void)flags; - _klogf("failing open(\"%s\")", path); - return errno = ENOSYS, -1; -} - -int fcntl(int fd, int cmd, ...) { - va_list argp; - va_start(argp, cmd); - if (cmd == F_DUPFD) { - int to = va_arg(argp, int); - va_end(argp); - return _sys_dup(fd, to, DUP_SEARCH); - } else { - va_end(argp); - _klogf("failing fcntl(%d)", cmd); - return errno = ENOSYS, -1; - } -} diff --git a/src/user/lib/fs/dir.c b/src/user/lib/fs/dir.c deleted file mode 100644 index b7f840d..0000000 --- a/src/user/lib/fs/dir.c +++ /dev/null @@ -1,84 +0,0 @@ -#include <camellia/syscalls.h> -#include <errno.h> -#include <limits.h> -#include <string.h> -#include <camellia/fs/dir.h> - -void dir_start(struct dirbuild *db, long offset, char *buf, size_t buflen) { - db->offset = offset; - db->buf = buf; - db->bpos = 0; - db->blen = buflen; - db->error = 0; - - // TODO decide how negative directory offsets should be handled - if (offset < 0) db->error = -ENOSYS; -} - -bool dir_append(struct dirbuild *db, const char *name) { - return dir_appendl(db, name, strlen(name)); -} - -bool dir_appendl(struct dirbuild *db, const char *name, size_t len) { - if (db->error) return true; - if (len > (size_t)LONG_MAX) { - db->error = -1; - return true; - } - - len++; // account for the null byte - - if (db->offset < (long)len) { - name += db->offset; - len -= db->offset; - db->offset = 0; - - if (db->buf) { - // TODO no buffer overrun check - memcpy(db->buf + db->bpos, name, len - 1); - db->buf[db->bpos + len - 1] = '\0'; - } - db->bpos += len; - } else { - db->offset -= len; - } - return false; -} - -bool dir_append_from(struct dirbuild *db, hid_t h) { - if (db->error) return true; - if (db->buf && db->bpos == db->blen) return false; - - int ret; - if (db->buf) { - ret = _sys_read(h, db->buf + db->bpos, db->blen - db->bpos, db->offset); - if (ret < 0) { - db->error = ret; - return true; - } else if (ret > 0) { - /* not eof */ - db->offset = 0; - db->bpos += ret; - return false; - } /* else ret == 0, EOF, need getsize */ - } - - ret = _sys_getsize(h); - if (ret < 0) { - db->error = ret; - return true; - } - if (db->offset < ret) { - /* should only occur when !buf, otherwise leaks previous data from buf. - * TODO consider impact */ - db->bpos += ret - db->offset; - db->offset = 0; - } else { - db->offset -= ret; - } - return false; -} - -long dir_finish(struct dirbuild *db) { - return db->error ? db->error : db->bpos; -} diff --git a/src/user/lib/fs/dirinject.c b/src/user/lib/fs/dirinject.c deleted file mode 100644 index 9b08756..0000000 --- a/src/user/lib/fs/dirinject.c +++ /dev/null @@ -1,129 +0,0 @@ -#include <assert.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <string.h> -#include <sys/param.h> -#include <unistd.h> - -typedef struct Handle { - int delegate; - int plen; - char path[]; -} Handle; - -static int -dir_seglen(const char *path) -{ - /* if path contains /, return its position + 1 - * otherwise, return strlen */ - int len = 0; - while (path[len]) { - if (path[len] == '/') { - len++; - break; - } - len++; - } - return len; -} - -static int -find_injects(const char *injects[], const char *path, int plen, struct dirbuild *db) -{ - // TODO deduplicate - const char *inj; - int matches = 0; - assert(plen >= 1); - assert(path[plen-1] == '/'); - - while ((inj = *injects++)) { - int ilen = strlen(inj); - if (plen < ilen && memcmp(path, inj, plen) == 0) { - if (db) { - /* inj[plen-1] == '/' */ - const char *ent = inj + plen; - dir_appendl(db, ent, dir_seglen(ent)); - } - matches++; - } - } - return matches; -} - -void -fs_dirinject2(const char *injects[]) -{ - const size_t buflen = 4096; - char *buf = malloc(buflen); - if (!buf) exit(1); - - for (;;) { - struct ufs_request req; - hid_t reqh = _sys_fs_wait(buf, buflen, &req); - if (reqh < 0) break; - Handle *hndl = req.id; - switch (req.op) { - case VFSOP_OPEN: { - if (buf[req.len - 1] == '/') { - if (find_injects(injects, buf, req.len, NULL) > 0) { - /* opening a directory that we're injecting into */ - hndl = malloc(sizeof(Handle) + req.len); - if (hndl == NULL) { - _sys_fs_respond(reqh, NULL, -EGENERIC, 0); - break; - } - /* ignore errors from _sys_open */ - hndl->delegate = _sys_open(buf, req.len, req.flags); - hndl->plen = req.len; - memcpy(hndl->path, buf, hndl->plen); - _sys_fs_respond(reqh, hndl, 0, 0); - break; - } - } - /* default behaviour */ - forward_open(reqh, buf, req.len, req.flags); - break; - } - - case VFSOP_CLOSE: { - if (hndl->delegate >= 0) { - close(hndl->delegate); - } - free(hndl); - _sys_fs_respond(reqh, NULL, 0, 0); - break; - } - - case VFSOP_READ: - case VFSOP_GETSIZE: { - struct dirbuild db; - char *target = NULL; - if (req.op == VFSOP_READ) { - target = buf; - } - req.capacity = MIN(req.capacity, buflen); - - dir_start(&db, req.offset, target, req.capacity); - find_injects(injects, hndl->path, hndl->plen, &db); - if (hndl->delegate >= 0) { - dir_append_from(&db, hndl->delegate); - } - _sys_fs_respond(reqh, target, dir_finish(&db), 0); - break; - } - - default: { - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - } - exit(0); -} - -void -fs_dirinject(const char *path) { - fs_dirinject2((const char*[]){ path, NULL }); -} diff --git a/src/user/lib/fs/misc.c b/src/user/lib/fs/misc.c deleted file mode 100644 index 30e5ab4..0000000 --- a/src/user/lib/fs/misc.c +++ /dev/null @@ -1,160 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <camellia/compat.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> - -void forward_open(hid_t reqh, const char *path, long len, int flags) { - // TODO use threads - // TODO solve for more complex cases, e.g. fs_union - /* done in a separate thread/process because open() can block, - * but that should only hold the caller back, and not the fs driver. - * - * for example, running `httpd` in one term would prevent you from doing - * basically anything on the second term, because fs_dirinject would be - * stuck on open()ing the socket */ - if (!_sys_fork(FORK_NOREAP, NULL)) { - _sys_fs_respond(reqh, NULL, _sys_open(path, len, flags), FSR_DELEGATE); - exit(0); - } - close(reqh); -} - -void fs_passthru(const char *prefix) { - const size_t buflen = 1024; - char *buf = malloc(buflen); - int prefix_len = prefix ? strlen(prefix) : 0; - if (!buf) exit(1); - - for (;;) { - struct ufs_request res; - hid_t reqh = _sys_fs_wait(buf, buflen, &res); - if (reqh < 0) break; - switch (res.op) { - case VFSOP_OPEN: - if (prefix) { - if (prefix_len + res.len > buflen) { - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - - memmove(buf + prefix_len, buf, res.len); - memcpy(buf, prefix, prefix_len); - res.len += prefix_len; - } - forward_open(reqh, buf, res.len, res.flags); - break; - - default: - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - exit(0); -} - -void fs_union(const char **list) { - struct ufs_request res; - - /* the buffer is split into two halves: - * the second one is filled out with the path by fs_wait - * the first one is used for the prepended paths */ - const size_t buflen = 1024; - const size_t prelen = 512; - const size_t postlen = buflen - prelen; - char *pre = malloc(buflen); - char *post = pre + prelen; - long ret; - struct dirbuild db; - if (!pre) exit(1); - - while (!c0_fs_wait(post, postlen, &res)) { - switch (res.op) { - case VFSOP_OPEN: - if (res.len == 1) { /* root directory */ - c0_fs_respond(NULL, 0, 0); - break; - } - - ret = -1; - for (size_t i = 0; ret < 0 && list[i]; i++) { - const char *prefix = list[i]; - size_t prefixlen = strlen(prefix); // TODO cache - if (prefixlen > prelen) continue; - char *path = post - prefixlen; - memcpy(path, prefix, prefixlen); - - ret = _sys_open(path, prefixlen + res.len, res.flags); - - post[res.len] = '\0'; - } - if (ret < 0) ret = -1; - c0_fs_respond(NULL, ret, FSR_DELEGATE); - break; - - case VFSOP_READ: - case VFSOP_GETSIZE: - if (res.capacity > buflen) - res.capacity = buflen; - bool end = false; - char *target = res.op == VFSOP_READ ? pre : NULL; - dir_start(&db, res.offset, target, res.capacity); - for (size_t i = 0; !end && list[i]; i++) { - const char *prefix = list[i]; - size_t prefixlen = strlen(prefix); - // TODO only open the directories once - // TODO ensure trailing slash - hid_t h = _sys_open(prefix, prefixlen, OPEN_READ); - if (h < 0) continue; - end = end || dir_append_from(&db, h); - _sys_close(h); - } - c0_fs_respond(target, dir_finish(&db), 0); - break; - - default: - c0_fs_respond(NULL, -1, 0); - break; - } - } - exit(0); -} - -hid_t ufs_wait(char *buf, size_t len, struct ufs_request *req) { - hid_t reqh; - for (;;) { - reqh = _sys_fs_wait(buf, len, req); - if (reqh < 0) break; - if (req->op == VFSOP_OPEN) { - if (req->len == len) { - _sys_fs_respond(reqh, NULL, -ENAMETOOLONG, 0); - continue; - } - buf[req->len] = '\0'; - // TODO ensure passed paths don't have null bytes in them in the kernel - } - break; - } - return reqh; -} - -int mount_at(const char *path) { - hid_t h; - int ret = _sys_fork(FORK_NEWFS, &h); - if (ret == 0) { - _klogf("%s: impl", path); - setproctitle("%s", path); - } else if (ret > 0) { - _sys_mount(h, path, strlen(path)); - close(h); - } else { - _sys_mount(HANDLE_NULLFS, path, strlen(path)); - } - return ret; -} diff --git a/src/user/lib/fs/whitelist.c b/src/user/lib/fs/whitelist.c deleted file mode 100644 index 54a79c3..0000000 --- a/src/user/lib/fs/whitelist.c +++ /dev/null @@ -1,114 +0,0 @@ -#include <camellia/flags.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <camellia/fs/dir.h> -#include <camellia/fs/misc.h> - -static int dir_seglen2(const char *path, size_t len) { - /* returns the length of the first segment of the path, including the trailing / (if any). */ - for (size_t i = 0; i < len; i++) { - if (path[i] == '/') - return i + 1; - } - return len; -} - -/** @return the length of the path w/o suffixes */ -static size_t suffix_parse(const char *path, size_t len, bool *ro_ptr) { - bool ro = false; - if (len >= 3 && !memcmp(path + len - 3, ":ro", 3)) { - ro = true; - len -= 3; - } - if (ro_ptr) *ro_ptr = ro; - return len; -} - -/** Check if a path is a prefix of another path. */ -// TODO move to libc; tests -static bool prefix_match(const char *prefix, size_t plen, const char *full, size_t flen) { - if (flen < plen) return false; - if (flen == plen) - return memcmp(full, prefix, flen) == 0; - return plen >= 1 - && prefix[plen - 1] == '/' /* prefixes must point to one of the parent directories */ - && memcmp(full, prefix, plen) == 0; -} - -void fs_whitelist(const char **whitelist) { - const size_t buflen = 1024; - char *buf = malloc(buflen); - if (!buf) exit(1); - for (;;) { - struct ufs_request res; - hid_t reqh = _sys_fs_wait(buf, buflen, &res); - if (reqh < 0) break; - - char *ipath = res.id; /* the path of the open()ed directory */ - - switch (res.op) { - case VFSOP_OPEN: { - bool error = false; - bool passthru = false; - bool inject = false; - - for (const char **entry = whitelist; *entry; entry++) { - bool ro = false; - size_t entry_len = suffix_parse(*entry, strlen(*entry), &ro); - /* If *entry is a prefix of the opened path, pass the open() through. */ - if (prefix_match(*entry, entry_len, buf, res.len)) { - passthru = true; - if (ro && OPEN_WRITEABLE(res.flags)) - error = true; - break; - } - /* If the path is a prefix of *entry, we might need to inject a directory. */ - if (prefix_match(buf, res.len, *entry, entry_len)) { - inject = true; - } - } - if (error) { - _sys_fs_respond(reqh, NULL, -EACCES, 0); - } else if (passthru) { - forward_open(reqh, buf, res.len, res.flags); - } else if (inject) { - // TODO all the inject points could be precomputed - ipath = malloc(res.len + 1); - memcpy(ipath, buf, res.len); - ipath[res.len] = '\0'; - _sys_fs_respond(reqh, ipath, 0, 0); - } else { - _sys_fs_respond(reqh, NULL, -1, 0); - } - break; - } - case VFSOP_READ: - case VFSOP_GETSIZE: { - struct dirbuild db; - size_t ilen = strlen(ipath); - char *target = res.op == VFSOP_READ ? buf : NULL; - dir_start(&db, res.offset, target, buflen); - for (const char **entry = whitelist; *entry; entry++) { - // TODO could be precomputed too - size_t elen = suffix_parse(*entry, strlen(*entry), NULL); - if (ilen < elen && !memcmp(ipath, *entry, ilen)) - dir_appendl(&db, *entry + ilen, dir_seglen2(*entry + ilen, elen - ilen)); - } - _sys_fs_respond(reqh, target, dir_finish(&db), 0); - break; - } - case VFSOP_CLOSE: { - free(ipath); - _sys_fs_respond(reqh, NULL, 0, 0); - break; - } - default: { - _sys_fs_respond(reqh, NULL, -1, 0); - break; - } - } - } - exit(0); -} diff --git a/src/user/lib/include/__errno.h b/src/user/lib/include/__errno.h deleted file mode 100644 index 7551ce0..0000000 --- a/src/user/lib/include/__errno.h +++ /dev/null @@ -1,26 +0,0 @@ -/* generated by awk */ -#ifdef E -E( 1, "EGENERIC unknown error") -E( 2, "EFAULT") -E( 3, "EBADF bad file descriptor") -E( 4, "EINVAL") -E( 5, "ENOSYS unsupported") -E( 6, "ERANGE") -E( 7, "ENOMEM") -E( 8, "ENOENT") -E( 9, "ENOTEMPTY") -E( 10, "EACCES") -E( 11, "EMFILE all file descriptors taken") -E( 12, "ECONNRESET") -E( 13, "EPIPE") -E( 14, "ECHILD") -E(200, "EISDIR") -E(201, "ENAMETOOLONG") -E(202, "ENOTDIR") -E(203, "ELOOP") -E(204, "ENOEXEC") -E(205, "EINTR") -E(206, "EWOULDBLOCK") -E(207, "EEXIST") -E(208, "EAGAIN") -#endif diff --git a/src/user/lib/include/__errno.h.awk b/src/user/lib/include/__errno.h.awk deleted file mode 100644 index 6232835..0000000 --- a/src/user/lib/include/__errno.h.awk +++ /dev/null @@ -1,20 +0,0 @@ -BEGIN { - print "/* generated by awk */"; - print "#ifdef E"; -} - -END { - print "#endif"; -} - -/#define/ { - comment = $2; - num = $3; - # extract the comment, if present - if (index($0, "/*")) { - sub(/[^/]*\/\*/, ""); - sub(/ *\*\//, ""); - comment = comment $0; - } - printf "E(%3s, \"%s\")\n", num, comment; -} diff --git a/src/user/lib/include/_proc.h b/src/user/lib/include/_proc.h deleted file mode 100644 index 5f9c321..0000000 --- a/src/user/lib/include/_proc.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -struct _psdata { - /* Description of the process, see setprogname. - * Assumed to be null terminated. */ - char desc[1024]; - - /* Base offset where the executable was loaded. */ - void *base; -}; - -/* First allocated in bootstrap. - * Freed on every exec(), just to be immediately reallocated by _start2(). */ -static struct _psdata *const _psdata_loc = (void*)0x10000; diff --git a/src/user/lib/include/alloca.h b/src/user/lib/include/alloca.h deleted file mode 100644 index 9c7641c..0000000 --- a/src/user/lib/include/alloca.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -#include <stddef.h> -void *alloca(size_t size); diff --git a/src/user/lib/include/bits/file.h b/src/user/lib/include/bits/file.h deleted file mode 100644 index 63a31c4..0000000 --- a/src/user/lib/include/bits/file.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -typedef struct _LIBC_FILE FILE; diff --git a/src/user/lib/include/bits/panic.h b/src/user/lib/include/bits/panic.h deleted file mode 100644 index 91aec5f..0000000 --- a/src/user/lib/include/bits/panic.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include <stdio.h> -#include <stdlib.h> - -#define __libc_panic(...) do { fprintf(stderr, "__libc_panic @ %s:", __func__); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); abort(); } while (0) diff --git a/src/user/lib/include/camellia.h b/src/user/lib/include/camellia.h deleted file mode 100644 index 2e4998b..0000000 --- a/src/user/lib/include/camellia.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include <camellia/flags.h> -#include <camellia/types.h> - -hid_t camellia_open(const char *path, int flags); diff --git a/src/user/lib/include/camellia/compat.h b/src/user/lib/include/camellia/compat.h deleted file mode 100644 index a7c6f1f..0000000 --- a/src/user/lib/include/camellia/compat.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include <camellia/types.h> - -/* c0 - fs_wait returning a handle */ -long c0_fs_wait(char *buf, long len, struct ufs_request *res); -long c0_fs_respond(void *buf, long ret, int flags); diff --git a/src/user/lib/include/camellia/fs/dir.h b/src/user/lib/include/camellia/fs/dir.h deleted file mode 100644 index d34a652..0000000 --- a/src/user/lib/include/camellia/fs/dir.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include <camellia/types.h> -#include <stdbool.h> -#include <stddef.h> - -struct dirbuild { - long offset; - char *buf; - long bpos, blen; - long error; -}; - -void dir_start(struct dirbuild *db, long offset, char *buf, size_t buflen); -bool dir_append(struct dirbuild *db, const char *name); -bool dir_appendl(struct dirbuild *db, const char *name, size_t len); -bool dir_append_from(struct dirbuild *db, hid_t h); -long dir_finish(struct dirbuild *db); diff --git a/src/user/lib/include/camellia/fs/misc.h b/src/user/lib/include/camellia/fs/misc.h deleted file mode 100644 index 301c604..0000000 --- a/src/user/lib/include/camellia/fs/misc.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include <stdbool.h> -#include <stdlib.h> - -void forward_open(hid_t reqh, const char *path, long len, int flags); - -void fs_passthru(const char *prefix); -void fs_whitelist(const char **list); -void fs_union(const char **list); - -void fs_dirinject(const char *path); -void fs_dirinject2(const char *injects[]); - -int mount_at(const char *path); - -// TODO separate fs drivers and wrappers around syscalls - -/** like _sys_fs_wait, but ensures *buf is a null terminated string on VFSOP_OPEN */ -hid_t ufs_wait(char *buf, size_t len, struct ufs_request *req); - -/** Mounts something and injects its path into the fs */ -#define MOUNT_AT(path) for (; mount_at(path) == 0; exit(1)) diff --git a/src/user/lib/include/ctype.h b/src/user/lib/include/ctype.h deleted file mode 100644 index 1ebb111..0000000 --- a/src/user/lib/include/ctype.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -int isalnum(int c); -int isalpha(int c); -int iscntrl(int c); -int isdigit(int c); -int isgraph(int c); -int islower(int c); -int isprint(int c); -int ispunct(int c); -int isspace(int c); -int isupper(int c); -int isxdigit(int c); - -int tolower(int c); -int toupper(int c); diff --git a/src/user/lib/include/dirent.h b/src/user/lib/include/dirent.h deleted file mode 100644 index 7c419d7..0000000 --- a/src/user/lib/include/dirent.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include <stdio.h> - -struct dirent { - ino_t d_ino; - char d_name[256]; /* NAME_MAX + 1 */ -}; - -typedef struct { - FILE *fp; - struct dirent dent; -} DIR; - -DIR *opendir(const char *name); -int closedir(DIR *dir); -struct dirent *readdir(DIR *dir); diff --git a/src/user/lib/include/draw.h b/src/user/lib/include/draw.h deleted file mode 100644 index 5e614be..0000000 --- a/src/user/lib/include/draw.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include <camellia/types.h> -#include <stddef.h> -#include <stdint.h> - -struct framebuf { - size_t len, width, height, pitch; - uint8_t bpp; - char *b; - - hid_t fd; -}; - -struct rect { uint32_t x1, y1, x2, y2; }; -void dirty_reset(struct rect *d); -void dirty_mark(struct rect *d, uint32_t x, uint32_t y); -void dirty_flush(struct rect *d, struct framebuf *fb); - -int fb_setup(struct framebuf *fb, const char *base); -int fb_anon(struct framebuf *fb, size_t w, size_t h); -uint32_t *fb_pixel(struct framebuf *fb, uint32_t x, uint32_t y); -void fb_cpy( - struct framebuf *dest, const struct framebuf *src, - size_t xd, size_t yd, size_t xs, size_t ys, size_t w, size_t h); diff --git a/src/user/lib/include/elfload.h b/src/user/lib/include/elfload.h deleted file mode 100644 index 825f765..0000000 --- a/src/user/lib/include/elfload.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include <bits/file.h> - -struct execdata { - int argc; - char **argv, **envp; - char *cwd; -}; - -void elf_execf(FILE *f, char **argv, char **envp); -void elf_exec(void *base, char **argv, char **envp); -void *elf_partialexec(void *elf); /* returns pointer to entry point */ - -void elf_selfreloc(void); // elfreloc.c diff --git a/src/user/lib/include/err.h b/src/user/lib/include/err.h deleted file mode 100644 index 6b63c6c..0000000 --- a/src/user/lib/include/err.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include <stdarg.h> - -_Noreturn void err(int ret, const char *fmt, ...); -_Noreturn void errx(int ret, const char *fmt, ...); -void warn(const char *fmt, ...); -void warnx(const char *fmt, ...); - -void vwarn(const char *fmt, va_list args); -void vwarnx(const char *fmt, va_list args); diff --git a/src/user/lib/include/errno.h b/src/user/lib/include/errno.h deleted file mode 100644 index 6686a01..0000000 --- a/src/user/lib/include/errno.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -#include <camellia/errno.h> -extern int errno; diff --git a/src/user/lib/include/esemaphore.h b/src/user/lib/include/esemaphore.h deleted file mode 100644 index 9cc85e0..0000000 --- a/src/user/lib/include/esemaphore.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include <camellia/types.h> - -struct evil_sem { - hid_t wait, signal; -}; - -void esem_signal(struct evil_sem *sem); -void esem_wait(struct evil_sem *sem); - -struct evil_sem *esem_new(int value); -void esem_free(struct evil_sem *sem); diff --git a/src/user/lib/include/fcntl.h b/src/user/lib/include/fcntl.h deleted file mode 100644 index 6338d1f..0000000 --- a/src/user/lib/include/fcntl.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#define F_SETFL 1 -#define F_GETFL 2 -#define F_DUPFD 3 -#define F_SETFD 4 - -#define FD_CLOEXEC 1 - -#define O_APPEND 0 -#define O_CREAT 0 -#define O_EXCL 0 -#define O_NONBLOCK 0 -#define O_RDONLY 0 -#define O_RDWR 0 -#define O_TRUNC 0 -#define O_WRONLY 0 - -#define R_OK 1 -#define W_OK 2 -#define X_OK 4 - -/* it can either take an additonal mode_t argument or none */ -int open(const char *path, int flags, ...); -int fcntl(int fd, int cmd, ...); diff --git a/src/user/lib/include/ftw.h b/src/user/lib/include/ftw.h deleted file mode 100644 index 6dc8132..0000000 --- a/src/user/lib/include/ftw.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include <sys/stat.h> - -int ftw(const char *dirpath, - int (*fn)(const char *fpath, const struct stat *sb, int typeflag), - int nopenfd); diff --git a/src/user/lib/include/getopt.h b/src/user/lib/include/getopt.h deleted file mode 120000 index 4890ceb..0000000 --- a/src/user/lib/include/getopt.h +++ /dev/null @@ -1 +0,0 @@ -../vendor/getopt/getopt.h
\ No newline at end of file diff --git a/src/user/lib/include/grp.h b/src/user/lib/include/grp.h deleted file mode 100644 index e7b99c9..0000000 --- a/src/user/lib/include/grp.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include <sys/types.h> - -struct group { - char *gr_name; - char *gr_passwd; - gid_t gr_gid; - char **gr_mem; -}; - -struct group *getgrgid(gid_t gid); diff --git a/src/user/lib/include/inttypes.h b/src/user/lib/include/inttypes.h deleted file mode 100644 index 9a6118b..0000000 --- a/src/user/lib/include/inttypes.h +++ /dev/null @@ -1 +0,0 @@ -#include <stdint.h> diff --git a/src/user/lib/include/limits.h b/src/user/lib/include/limits.h deleted file mode 100644 index 972553f..0000000 --- a/src/user/lib/include/limits.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include <camellia/path.h> // just for PATH_MAX -// #include_next <limits.h> - -#define _POSIX2_RE_DUP_MAX 255 -#define NAME_MAX 255 diff --git a/src/user/lib/include/locale.h b/src/user/lib/include/locale.h deleted file mode 100644 index 1221375..0000000 --- a/src/user/lib/include/locale.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once -#include <limits.h> - -#define LC_ALL 0 -#define LC_COLLATE 1 -#define LC_CTYPE 2 -#define LC_MESSAGES 3 -#define LC_MONETARY 4 -#define LC_NUMERIC 5 -#define LC_TIME 6 - -struct lconv { - char *decimal_point; - char *thousands_sep; - char *grouping; - char *mon_decimal_point; - char *mon_thousands_sep; - char *mon_grouping; - char *positive_sign; - char *negative_sign; - char *currency_symbol; - char frac_digits; - char p_cs_precedes; - char n_cs_precedes; - char p_sep_by_space; - char n_sep_by_space; - char p_sign_posn; - char n_sign_posn; - char *int_curr_symbol; - char int_frac_digits; - char int_p_cs_precedes; - char int_n_cs_precedes; - char int_p_sep_by_space; - char int_n_sep_by_space; - char int_p_sign_posn; - char int_n_sign_posn; -}; - -static inline struct lconv *localeconv(void) { - /* per Linux's lconv(3) */ - static struct lconv locale = (struct lconv){ - .decimal_point = ".", - .thousands_sep = "", - .grouping = "", - .mon_decimal_point = "", - .mon_thousands_sep = "", - .mon_grouping = "", - .positive_sign = "", - .negative_sign = "", - .currency_symbol = "", - .frac_digits = CHAR_MAX, - .p_cs_precedes = CHAR_MAX, - .n_cs_precedes = CHAR_MAX, - .p_sep_by_space = CHAR_MAX, - .n_sep_by_space = CHAR_MAX, - .p_sign_posn = CHAR_MAX, - .n_sign_posn = CHAR_MAX, - .int_curr_symbol = "", - .int_frac_digits = CHAR_MAX, - .int_p_cs_precedes = CHAR_MAX, - .int_n_cs_precedes = CHAR_MAX, - .int_p_sep_by_space = CHAR_MAX, - .int_n_sep_by_space = CHAR_MAX, - .int_p_sign_posn = CHAR_MAX, - .int_n_sign_posn = CHAR_MAX, - }; - return &locale; -} - -static inline char *setlocale(int category, const char *locale) { - (void)category; (void)locale; - return NULL; -} diff --git a/src/user/lib/include/malloc.h b/src/user/lib/include/malloc.h deleted file mode 120000 index 80b9bf5..0000000 --- a/src/user/lib/include/malloc.h +++ /dev/null @@ -1 +0,0 @@ -../vendor/dlmalloc/malloc.h
\ No newline at end of file diff --git a/src/user/lib/include/math.h b/src/user/lib/include/math.h deleted file mode 100644 index 1aec564..0000000 --- a/src/user/lib/include/math.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#define INFINITY __builtin_inff() -#define HUGE_VAL ((double)INFINITY) - -double acos(double x); -double asin(double x); -double atan2(double x, double y); -double cos(double x); -double cosh(double x); -double sin(double x); -double sinh(double x); -double tan(double x); -double tanh(double x); - -double fabs(double x); -double floor(double x); -double ceil(double x); -double log(double x); -double log2(double x); -double log10(double x); -double exp(double x); -double fmod(double x, double y); -double frexp(double num, int *exp); -double ldexp(double x, int exp); -double pow(double x, double y); -double sqrt(double x); diff --git a/src/user/lib/include/pwd.h b/src/user/lib/include/pwd.h deleted file mode 100644 index 6721ca2..0000000 --- a/src/user/lib/include/pwd.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include <sys/types.h> - -struct passwd { - char *pw_name; - char *pw_passwd; - uid_t pw_uid; - gid_t pw_gid; - char *pw_gecos; - char *pw_dir; - char *pw_shell; -}; - -struct passwd *getpwuid(uid_t uid); diff --git a/src/user/lib/include/setjmp.h b/src/user/lib/include/setjmp.h deleted file mode 100644 index 6d05d79..0000000 --- a/src/user/lib/include/setjmp.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include <bits/panic.h> - -typedef uint64_t jmp_buf[8]; /* rbx, rsp, rbp, r12, r13, r14, r15, rip */ -typedef char sigjmp_buf[1]; - -int setjmp(jmp_buf env); -_Noreturn void longjmp(jmp_buf env, int val); - -static inline int sigsetjmp(sigjmp_buf env, int savemask) { - (void)env; (void)savemask; - return 0; -} - -static inline _Noreturn void siglongjmp(sigjmp_buf env, int val) { - (void)env; (void)val; - __libc_panic("unimplemented"); -} diff --git a/src/user/lib/include/signal.h b/src/user/lib/include/signal.h deleted file mode 100644 index 012481e..0000000 --- a/src/user/lib/include/signal.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include <bits/panic.h> -#include <sys/types.h> -#include <errno.h> // only for ENOSYS - -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGFPE 8 -#define SIGKILL 9 -#define SIGSEGV 11 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 - -#define SIGCONT 16 -#define SIGPIPE 17 -#define SIGTSTP 18 -#define SIGTTIN 19 -#define SIGTTOU 20 -#define SIGWINCH 21 -#define SIGCHLD 22 - -#define NSIG 32 - -#define SIG_DFL 0 -#define SIG_ERR 0 -#define SIG_IGN 0 -#define SIG_SETMASK 0 - -typedef int sig_atomic_t; -typedef struct {} sigset_t; -typedef struct {} siginfo_t; -extern const char *const sys_siglist[]; - -struct sigaction { - void (*sa_handler)(int); - void (*sa_sigaction)(int, siginfo_t *, void *); - sigset_t sa_mask; - int sa_flags; - void (*sa_restorer)(void); -}; - -int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact); -int sigemptyset(sigset_t *set); -int sigfillset(sigset_t *set); -int sigprocmask(int how, const sigset_t *set, const sigset_t *oldset); -int sigsuspend(const sigset_t *mask); -int signal(int sig, void (*func)(int)); -int kill(pid_t pid, int sig); -int raise(int sig); diff --git a/src/user/lib/include/stdio.h b/src/user/lib/include/stdio.h deleted file mode 100644 index 48d5058..0000000 --- a/src/user/lib/include/stdio.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once -#include <bits/file.h> -#include <stdarg.h> -#include <stddef.h> -#include <sys/types.h> - -#define EOF (-1) -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 - -#define SEEK_SET 1 -#define SEEK_CUR 2 -#define SEEK_END 3 - -#define _IONBF 0 -#define _IOFBF 1 -#define _IOLBF 2 - -#define BUFSIZ 4096 - -/* stop fread() from trying to fill the entire buffer before returning - * i.e. it will call _sys_read() exactly once */ -#define FEXT_NOFILL 1 - -int printf(const char *restrict fmt, ...); -int fprintf(FILE *restrict f, const char *restrict fmt, ...); - -int sprintf(char *restrict s, const char *restrict fmt, ...); - -int vprintf(const char *restrict fmt, va_list ap); -int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap); - -int _klogf(const char *fmt, ...); // for kernel debugging only - - -extern FILE *const stdin, *const stdout, *const stderr; - -FILE *fopen(const char *path, const char *mode); -FILE *freopen(const char *path, const char *mode, FILE *); -FILE *fdopen(int fd, const char *mode); -FILE *file_clone(const FILE *, const char *mode); -FILE *popen(const char *cmd, const char *mode); -int pclose(FILE *f); -FILE *tmpfile(void); - -int fextflags(FILE *, int extflags); -int setvbuf(FILE *restrict f, char *restrict buf, int type, size_t size); -int fclose(FILE *); -int fflush(FILE *f); - -size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict); -size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict); -int fputs(const char *s, FILE *f); -char *fgets(char *buf, int size, FILE *f); -int fgetc(FILE *f); -int getc(FILE *f); -int fputc(int c, FILE *f); -int putc(int c, FILE *f); -int ungetc(int c, FILE *f); - -int fseek(FILE *f, long offset, int whence); -int fseeko(FILE *f, off_t offset, int whence); -long ftell(FILE *f); -off_t ftello(FILE *f); - -int feof(FILE *); -int ferror(FILE *); -void clearerr(FILE *f); - -void perror(const char *s); -int puts(const char *s); -int getchar(void); -int putchar(int c); - -off_t lseek(int fd, off_t off, int whence); - -int remove(const char *path); -int rename(const char *old, const char *new); - -#define L_tmpnam (5 + 16 + 1) -char *tmpnam(char *s); - -int sscanf(const char *restrict s, const char *restrict format, ...); diff --git a/src/user/lib/include/stdlib.h b/src/user/lib/include/stdlib.h deleted file mode 100644 index ee9d179..0000000 --- a/src/user/lib/include/stdlib.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include <stddef.h> -#include <stdlib.h> - -#ifndef NO_MALLOC_H -#include <malloc.h> -#endif - -#define EXIT_SUCCESS 0 -#define EXIT_FAILURE 1 - -_Noreturn void abort(void); -_Noreturn void exit(int); - -const char *getprogname(void); -void setprogname(const char *progname); -void setproctitle(const char *fmt, ...); - -int mkstemp(char *template); -char *getenv(const char *name); -int system(const char *cmd); - -int abs(int i); - -int atoi(const char *s); -double atof(const char *s); - -long strtol(const char *restrict s, char **restrict end, int base); -long long strtoll(const char *restrict s, char **restrict end, int base); -unsigned long strtoul(const char *restrict s, char **restrict end, int base); -unsigned long long strtoull(const char *restrict s, char **restrict end, int base); -double strtod(const char *restrict s, char **restrict end); - -void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *a, const void *b)); diff --git a/src/user/lib/include/string.h b/src/user/lib/include/string.h deleted file mode 100644 index 78bed9b..0000000 --- a/src/user/lib/include/string.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include <shared/mem.h> -#include <strings.h> /* work around bad include in dash */ - -char *strchr(const char *s, int c); -char *strrchr(const char *s, int c); - -size_t strspn(const char *s, const char *accept); -size_t strcspn(const char *s, const char *reject); -char *strpbrk(const char *s1, const char *s2); - -char *strtok(char *restrict s, const char *restrict sep); -char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state); - -int strncmp(const char *s1, const char *s2, size_t n); -int strcoll(const char *s1, const char *s2); - -char *strstr(const char *s1, const char *s2); - -char *strcpy(char *restrict s1, const char *restrict s2); -char *strncpy(char *restrict s1, const char *restrict s2, size_t n); -char *stpncpy(char *restrict dst, const char *restrict src, size_t n); -char *strdup(const char *s); - -char *strerror(int errnum); diff --git a/src/user/lib/include/strings.h b/src/user/lib/include/strings.h deleted file mode 100644 index d0abc47..0000000 --- a/src/user/lib/include/strings.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include <stddef.h> - -int strcasecmp(const char *s1, const char *s2); -int strncasecmp(const char *s1, const char *s2, size_t n); diff --git a/src/user/lib/include/sys/ioctl.h b/src/user/lib/include/sys/ioctl.h deleted file mode 100644 index 708bc3f..0000000 --- a/src/user/lib/include/sys/ioctl.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include <errno.h> // only for ENOSYS - -#define TIOCGWINSZ 0 -struct winsize { - int ws_row, ws_col; -}; - -static inline int ioctl(int fd, int req, ...) { - (void)fd; (void)req; - errno = ENOSYS; - return -1; -} diff --git a/src/user/lib/include/sys/mman.h b/src/user/lib/include/sys/mman.h deleted file mode 100644 index 074ebe2..0000000 --- a/src/user/lib/include/sys/mman.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include <camellia/flags.h> -#include <sys/types.h> - -#define MMAP_UNSUPPORTED 0xFFFF - -#define PROT_EXEC 1 -#define PROT_NONE MMAP_UNSUPPORTED -#define PROT_READ 1 -#define PROT_WRITE 1 - -#define MAP_FIXED MMAP_UNSUPPORTED -#define MAP_PRIVATE 0 -#define MAP_SHARED MMAP_UNSUPPORTED -#define MAP_ANONYMOUS 1 - -void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off); -int munmap(void *addr, size_t len); diff --git a/src/user/lib/include/sys/param.h b/src/user/lib/include/sys/param.h deleted file mode 100644 index e6c9d6f..0000000 --- a/src/user/lib/include/sys/param.h +++ /dev/null @@ -1,2 +0,0 @@ -#define MIN(a, b) ((a)<(b) ? (a):(b)) -#define MAX(a, b) ((a)<(b) ? (b):(a)) diff --git a/src/user/lib/include/sys/resource.h b/src/user/lib/include/sys/resource.h deleted file mode 100644 index 4582ce0..0000000 --- a/src/user/lib/include/sys/resource.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -struct rusage {}; diff --git a/src/user/lib/include/sys/stat.h b/src/user/lib/include/sys/stat.h deleted file mode 100644 index 343db55..0000000 --- a/src/user/lib/include/sys/stat.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once -#include <bits/panic.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> // struct timespec -#include <errno.h> // only for ENOSYS - -struct stat { - dev_t st_dev; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - struct timespec st_atim; - struct timespec st_mtim; - struct timespec st_ctim; - -#define st_atime st_atim.tv_sec -#define st_mtime st_mtim.tv_sec -#define st_ctime st_ctim.tv_sec -}; - -#define S_IFMT 0170000 -#define S_IFSOCK 0140000 -#define S_IFLNK 0120000 -#define S_IFREG 0100000 -#define S_IFBLK 0060000 -#define S_IFDIR 0040000 -#define S_IFCHR 0020000 -#define S_IFIFO 0010000 -#define S_ISUID 04000 -#define S_ISGID 02000 -#define S_ISVTX 01000 - -/* inode(7) */ -#define S_ISREG(m) ((m & S_IFMT) == S_IFREG) -#define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) -#define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR) -#define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK) -#define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO) -#define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK) -#define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK) - -int fstat(int fd, struct stat *sb); -int stat(const char *restrict path, struct stat *restrict sb); -int lstat(const char *restrict path, struct stat *restrict sb); -int mkdir(const char *path, mode_t mode); - -static inline mode_t umask(mode_t mask) { - (void)mask; - __libc_panic("unimplemented"); -} - -static inline int chmod(const char *path, mode_t mode) { - (void)path; (void)mode; - errno = ENOSYS; - return -1; -} - -static inline int mknod(const char *path, mode_t mode, dev_t dev) { - (void)path; (void)mode; (void)dev; - errno = ENOSYS; - return -1; -} diff --git a/src/user/lib/include/sys/sysmacros.h b/src/user/lib/include/sys/sysmacros.h deleted file mode 100644 index 30e0efd..0000000 --- a/src/user/lib/include/sys/sysmacros.h +++ /dev/null @@ -1,3 +0,0 @@ -#define makedev(maj, min) 0 -#define major(x) 0 -#define minor(x) 0 diff --git a/src/user/lib/include/sys/time.h b/src/user/lib/include/sys/time.h deleted file mode 100644 index e69de29..0000000 --- a/src/user/lib/include/sys/time.h +++ /dev/null diff --git a/src/user/lib/include/sys/times.h b/src/user/lib/include/sys/times.h deleted file mode 100644 index 4a8d3ef..0000000 --- a/src/user/lib/include/sys/times.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include <bits/panic.h> - -struct tms { - clock_t tms_utime; - clock_t tms_stime; - clock_t tms_cutime; - clock_t tms_cstime; -}; - -static inline clock_t times(struct tms *buf) { - __libc_panic("unimplemented"); -} diff --git a/src/user/lib/include/sys/types.h b/src/user/lib/include/sys/types.h deleted file mode 100644 index faf656a..0000000 --- a/src/user/lib/include/sys/types.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include <stddef.h> -#include <stdint.h> - -typedef long long off_t; -typedef int64_t time_t; -typedef uint64_t clock_t; -typedef int mode_t; - -typedef int dev_t; -typedef int ino_t; -typedef int mode_t; -typedef int nlink_t; -typedef int uid_t; -typedef int gid_t; -typedef int blksize_t; -typedef int blkcnt_t; -typedef int pid_t; diff --git a/src/user/lib/include/sys/wait.h b/src/user/lib/include/sys/wait.h deleted file mode 100644 index cff407e..0000000 --- a/src/user/lib/include/sys/wait.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include <sys/types.h> - -#define WIFSTOPPED(x) 0 -#define WEXITSTATUS(x) ((x)&0xFF) -#define WIFEXITED(x) 1 -#define WSTOPSIG(x) 0 -#define WTERMSIG(x) 0 - -#define WNOHANG 0 -#define WUNTRACED 0 - -pid_t wait3(int *wstatus, int opts, struct rusage *rusage); diff --git a/src/user/lib/include/termios.h b/src/user/lib/include/termios.h deleted file mode 100644 index e69de29..0000000 --- a/src/user/lib/include/termios.h +++ /dev/null diff --git a/src/user/lib/include/thread.h b/src/user/lib/include/thread.h deleted file mode 100644 index 5a5ddc0..0000000 --- a/src/user/lib/include/thread.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include <stdlib.h> - -void thread_creates(int flags, void (*fn)(void*), void *arg, void *stack); - -static inline void thread_create(int flags, void (*fn)(void*), void *arg) { - /* error checking is for WIMPS. */ - thread_creates(flags, fn, arg, malloc(4096) + 4096); -} diff --git a/src/user/lib/include/time.h b/src/user/lib/include/time.h deleted file mode 100644 index 5d03664..0000000 --- a/src/user/lib/include/time.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include <sys/types.h> - -#define CLOCKS_PER_SEC 1000000 - -struct tm { - int tm_sec; /* Seconds [0,60]. */ - int tm_min; /* Minutes [0,59]. */ - int tm_hour; /* Hour [0,23]. */ - int tm_mday; /* Day of month [1,31]. */ - int tm_mon; /* Month of year [0,11]. */ - int tm_year; /* Years since 1900. */ - int tm_wday; /* Day of week [0,6] (Sunday =0). */ - int tm_yday; /* Day of year [0,365]. */ - int tm_isdst; /* Daylight Savings flag. */ -}; - -struct timespec { - time_t tv_sec; - long long tv_nsec; -}; - -time_t time(time_t *tloc); -clock_t clock(void); - -struct tm *gmtime(const time_t *timer); -struct tm *localtime(const time_t *timer); -time_t mktime(struct tm *timeptr); - -double difftime(time_t time1, time_t time0); - -size_t strftime( - char *restrict s, size_t maxsize, - const char *restrict format, const struct tm *restrict timeptr); diff --git a/src/user/lib/include/unistd.h b/src/user/lib/include/unistd.h deleted file mode 100644 index 005e79c..0000000 --- a/src/user/lib/include/unistd.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include <camellia/types.h> // TODO only needed because of hid_t -#include <sys/types.h> -#include <getopt.h> - -// TODO custom stdint.h, ssize_t doesn't belong here -typedef long long ssize_t; - -extern char **environ; - -int fork(void); -pid_t vfork(void); -int close(hid_t h); -_Noreturn void _exit(int); - -ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize); -int link(const char *path1, const char *path2); -int unlink(const char *path); -int symlink(const char *path1, const char *path2); -int isatty(int fd); - -int execv(const char *path, char *const argv[]); -int execve(const char *path, char *const argv[], char *const envp[]); - -int chdir(const char *path); -char *getcwd(char *buf, size_t size); - -uid_t getuid(void); -uid_t geteuid(void); -gid_t getgid(void); -gid_t getegid(void); - -int chown(const char *path, uid_t owner, gid_t group); - -int setpgid(pid_t pid, pid_t pgid); -pid_t tcgetpgrp(int fd); -int tcsetpgrp(int fd, pid_t pgrp); -pid_t getpgrp(void); -pid_t getpid(void); -pid_t getppid(void); - -int getgroups(int size, gid_t list[]); - -ssize_t read(int fd, void *buf, size_t count); -ssize_t write(int fd, const void *buf, size_t count); -int pipe(int pipefd[2]); -int dup2(int oldfd, int newfd); - -/* Converts a relative path to an absolute one, simplifying it if possible. - * If in == NULL - return the length of cwd. Doesn't include the trailing slash, - * except for the root dir. Includes the null byte. - * If size isn't enough to fit the path, returns the amount of bytes needed to fit - * it, including the null byte. - * - * Note that some errors are only detected if *out != NULL, so you must check the return - * value twice. - * @return 0 on failure, length of the path otherwise */ -size_t absolutepath(char *out, const char *in, size_t size); - -// TODO put in an internal libc header -void __setinitialcwd(const char *c); - -void intr_set(void (*fn)(void)); -void intr_default(void); diff --git a/src/user/lib/intr.s b/src/user/lib/intr.s deleted file mode 100644 index 008387d..0000000 --- a/src/user/lib/intr.s +++ /dev/null @@ -1,20 +0,0 @@ -.section .text -.global intr_trampoline -.type intr_trampoline, @function -intr_trampoline: - push %rax - push %rdx - call *_intr(%rip) - pop %rdx - pop %rax - pop tmprip(%rip) - pop %rsp - jmp *tmprip(%rip) - -.section .bss -tmprip: - .skip 8 - -.global _intr -_intr: - .skip 8 diff --git a/src/user/lib/math.c b/src/user/lib/math.c deleted file mode 100644 index 4f8eda4..0000000 --- a/src/user/lib/math.c +++ /dev/null @@ -1,27 +0,0 @@ -#include <math.h> -#include <bits/panic.h> - -// TODO port a libm -#pragma GCC diagnostic ignored "-Wunused-parameter" -double acos(double x) { __libc_panic("unimplemented"); } -double asin(double x) { __libc_panic("unimplemented"); } -double atan2(double x, double y) { __libc_panic("unimplemented"); } -double cos(double x) { __libc_panic("unimplemented"); } -double cosh(double x) { __libc_panic("unimplemented"); } -double sin(double x) { __libc_panic("unimplemented"); } -double sinh(double x) { __libc_panic("unimplemented"); } -double tan(double x) { __libc_panic("unimplemented"); } -double tanh(double x) { __libc_panic("unimplemented"); } - -double fabs(double x) { __libc_panic("unimplemented"); } -double floor(double x) { __libc_panic("unimplemented"); } -double ceil(double x) { __libc_panic("unimplemented"); } -double log(double x) { __libc_panic("unimplemented"); } -double log2(double x) { __libc_panic("unimplemented"); } -double log10(double x) { __libc_panic("unimplemented"); } -double exp(double x) { __libc_panic("unimplemented"); } -double fmod(double x, double y) { __libc_panic("unimplemented"); } -double frexp(double num, int *exp) { __libc_panic("unimplemented"); } -double ldexp(double x, int exp) { __libc_panic("unimplemented"); } -double pow(double x, double y) { __libc_panic("unimplemented"); } -double sqrt(double x) { __libc_panic("unimplemented"); } diff --git a/src/user/lib/mman.c b/src/user/lib/mman.c deleted file mode 100644 index 32eeb2a..0000000 --- a/src/user/lib/mman.c +++ /dev/null @@ -1,24 +0,0 @@ -/* mmap() emulation */ -#include <camellia/syscalls.h> -#include <errno.h> -#include <sys/mman.h> - -void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) { - (void)fd; (void) off; - if ((flags & MMAP_UNSUPPORTED) == MMAP_UNSUPPORTED || - (prot & MMAP_UNSUPPORTED) == MMAP_UNSUPPORTED || - !(flags & MAP_ANONYMOUS)) - { - errno = ENOSYS; - return NULL; - } - - void *p = _sys_memflag(addr, len, MEMFLAG_FINDFREE | MEMFLAG_PRESENT); - if (!p) errno = ENOMEM; - return p; -} - -int munmap(void *addr, size_t len) { - _sys_memflag(addr, len, 0); - return 0; -} diff --git a/src/user/lib/printf.c b/src/user/lib/printf.c deleted file mode 100644 index ad1fd06..0000000 --- a/src/user/lib/printf.c +++ /dev/null @@ -1,56 +0,0 @@ -#include <camellia/syscalls.h> -#include <shared/printf.h> -#include <stdio.h> -#include <string.h> - - -static void backend_file(void *arg, const char *buf, size_t len) { - fwrite(buf, 1, len, arg); -} - -int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) { - return __printf_internal(fmt, ap, backend_file, f); -} - - -int printf(const char *restrict fmt, ...) { - int ret; - va_list argp; - va_start(argp, fmt); - ret = vprintf(fmt, argp); - va_end(argp); - return ret; -} - -int fprintf(FILE *restrict f, const char *restrict fmt, ...) { - int ret; - va_list argp; - va_start(argp, fmt); - ret = vfprintf(f, fmt, argp); - va_end(argp); - return ret; -} - -int sprintf(char *restrict s, const char *restrict fmt, ...) { - int ret; - va_list argp; - va_start(argp, fmt); - ret = vsnprintf(s, ~0, fmt, argp); - va_end(argp); - return ret; -} - -int vprintf(const char *restrict fmt, va_list ap) { - return vfprintf(stdout, fmt, ap); -} - -int _klogf(const char *fmt, ...) { - char buf[256]; - int ret; - va_list argp; - va_start(argp, fmt); - ret = vsnprintf(buf, sizeof buf, fmt, argp); - va_end(argp); - _sys_debug_klog(buf, ret); - return ret; -} diff --git a/src/user/lib/pwd.c b/src/user/lib/pwd.c deleted file mode 100644 index d7bf2af..0000000 --- a/src/user/lib/pwd.c +++ /dev/null @@ -1,23 +0,0 @@ -#include <grp.h> -#include <pwd.h> -#include <string.h> - -struct passwd * -getpwuid(uid_t uid) -{ - static struct passwd p; - memset(&p, 0, sizeof p); - p.pw_name = "unnamed"; - p.pw_uid = uid; - return &p; -} - -struct group * -getgrgid(gid_t gid) -{ - static struct group g; - memset(&g, 0, sizeof g); - g.gr_name = "unnamed"; - g.gr_gid = gid; - return &g; -} diff --git a/src/user/lib/setjmp.s b/src/user/lib/setjmp.s deleted file mode 100644 index 29292da..0000000 --- a/src/user/lib/setjmp.s +++ /dev/null @@ -1,39 +0,0 @@ -.section .text -.global setjmp -.type setjmp, @function -// int setjmp(jmp_buf env); -setjmp: - mov %rbx, 0(%rdi) - mov %rbp, 16(%rdi) - mov %r12, 24(%rdi) - mov %r13, 32(%rdi) - mov %r14, 40(%rdi) - mov %r15, 48(%rdi) - /* save registers as if after a ret */ - lea 8(%rsp), %rax - mov %rax, 8(%rdi) - mov (%rsp), %rax - mov %rax, 56(%rdi) - xor %rax, %rax - - mov 8(%rdi), %rsp - jmp *56(%rdi) - - ret - - -.global longjmp -.type longjmp, @function -// _Noreturn void longjmp(jmp_buf env, int val); -longjmp: - mov %rsi, %rax - cmp $1, %rax /* carry set as for %rax - 1 - so, 1 only if %rax == 0 */ - adc $0, %rax -1: mov 0(%rdi), %rbx - mov 8(%rdi), %rsp - mov 16(%rdi), %rbp - mov 24(%rdi), %r12 - mov 32(%rdi), %r13 - mov 40(%rdi), %r14 - mov 48(%rdi), %r15 - jmp *56(%rdi) diff --git a/src/user/lib/signal.c b/src/user/lib/signal.c deleted file mode 100644 index 3200263..0000000 --- a/src/user/lib/signal.c +++ /dev/null @@ -1,100 +0,0 @@ -#include <bits/panic.h> -#include <signal.h> - -const char *const sys_siglist[] = { - NULL, - "SIGHUP", /* 1 */ - "SIGINT", /* 2 */ - "SIGQUIT", /* 3 */ - "SIGILL", /* 4 */ - "SIGTRAP", /* 5 */ - "SIGABRT", /* 6 */ - "SIGFPE", /* 8 */ - "SIGKILL", /* 9 */ - "SIGSEGV", /* 11 */ - "SIGPIPE", /* 13 */ - "SIGALRM", /* 14 */ - "SIGTERM", /* 15 */ - "SIGCONT", /* 16 */ - "SIGPIPE", /* 17 */ - "SIGTSTP", /* 18 */ - "SIGTTIN", /* 19 */ - "SIGTTOU", /* 20 */ - "SIGWINCH", /* 21 */ - "SIGCHLD", /* 22 */ -}; - -static struct sigaction sigaction_default = {}; -static struct sigaction *sigaction_current[] = { - NULL, - &sigaction_default, /* 1 */ - &sigaction_default, /* 2 */ - &sigaction_default, /* 3 */ - &sigaction_default, /* 4 */ - &sigaction_default, /* 5 */ - &sigaction_default, /* 6 */ - &sigaction_default, /* 8 */ - &sigaction_default, /* 9 */ - &sigaction_default, /* 11 */ - &sigaction_default, /* 13 */ - &sigaction_default, /* 14 */ - &sigaction_default, /* 15 */ - &sigaction_default, /* 16 */ - &sigaction_default, /* 17 */ - &sigaction_default, /* 18 */ - &sigaction_default, /* 19 */ - &sigaction_default, /* 20 */ - &sigaction_default, /* 21 */ - &sigaction_default, /* 22 */ -}; - -int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { - const int siglen = sizeof(sigaction_current) / sizeof(sigaction_current[0]); - if (sig >= siglen) { - return errno = EINVAL, -1; - } - if (oldact) { - oldact = sigaction_current[sig]; - } - if (act) { - if (sig == SIGKILL) { - return errno = EINVAL, -1; - } - sigaction_current[sig] = (void*)act; - } - return 0; -} - -int sigemptyset(sigset_t *set) { - (void)set; - return 0; -} - -int sigfillset(sigset_t *set) { - (void)set; - return 0; -} - -int sigprocmask(int how, const sigset_t *set, const sigset_t *oldset) { - return 0; -} - -int sigsuspend(const sigset_t *mask) { - (void)mask; - __libc_panic("unimplemented"); -} - -int signal(int sig, void (*func)(int)) { - (void)sig; (void)func; - return errno = ENOSYS, SIG_ERR; -} - -int kill(pid_t pid, int sig) { - (void)pid; (void)sig; - __libc_panic("unimplemented"); -} - -int raise(int sig) { - (void)sig; - __libc_panic("unimplemented"); -} diff --git a/src/user/lib/stdio/file.c b/src/user/lib/stdio/file.c deleted file mode 100644 index efaf013..0000000 --- a/src/user/lib/stdio/file.c +++ /dev/null @@ -1,359 +0,0 @@ -#include "file.h" -#include <bits/panic.h> -#include <camellia.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/param.h> -#include <unistd.h> - -static FILE _stdin_null = { .fd = STDIN_FILENO }; -static FILE _stdout_null = { .fd = STDOUT_FILENO }; -static FILE _stderr_null = { .fd = STDERR_FILENO }; -FILE *const stdin = &_stdin_null; -FILE *const stdout = &_stdout_null; -FILE *const stderr = &_stderr_null; - - -FILE *fopen(const char *path, const char *mode) { - FILE *f; - hid_t h; - int flags = 0; - if (!path) { - errno = 1; - return NULL; - } else if (path[0] == '!') { - /* special handling for "!files" */ - path++; - if (!strcmp(path, "stdin")) return file_clone(stdin, mode); - if (!strcmp(path, "stdout")) return file_clone(stdout, mode); - if (!strcmp(path, "stderr")) return file_clone(stderr, mode); - errno = ENOENT; - return NULL; - } - - if (strchr(mode, 'e')) { - /* camellia extension: open as executable */ - flags |= OPEN_EXEC; - } else if (strchr(mode, 'r')) { - flags |= OPEN_READ; - if (strchr(mode, '+')) - flags |= OPEN_WRITE; - } else { - flags |= OPEN_WRITE | OPEN_CREATE; - } - - h = camellia_open(path, flags); - if (h < 0) return NULL; - - if (mode[0] == 'w') - _sys_write(h, NULL, 0, 0, WRITE_TRUNCATE); - - f = fdopen(h, mode); - if (!f) close(h); - setvbuf(f, NULL, _IOFBF, 0); - return f; -} - - FILE *freopen(const char *path, const char *mode, FILE *f) { - /* partially based on the musl implementation of freopen */ - FILE *f2; - if (!path) goto fail; - f2 = fopen(path, mode); - if (!f2) goto fail; - - if (f->fd == f2->fd) { - f2->fd = -1; - } else { - if (_sys_dup(f2->fd, f->fd, 0) < 0) goto fail2; - } - f->pos = f2->pos; - f->eof = f2->eof; - fclose(f2); - return f; - -fail2: - fclose(f2); -fail: - fclose(f); - return NULL; -} - -FILE *fdopen(int fd, const char *mode) { - FILE *f; - f = calloc(1, sizeof *f); - if (f) { - f->fd = fd; - f->pos = mode[0] == 'a' ? -1 : 0; - } - return f; -} - -FILE *file_clone(const FILE *f, const char *mode) { - hid_t h = _sys_dup(f->fd, -1, 0); - FILE *f2; - if (h < 0) return NULL; - - f2 = fdopen(h, mode); - if (!f2) { - close(h); - return NULL; - } - f2->pos = f->pos; - f2->eof = f->eof; - f2->fd = h; - return f2; -} - -// TODO popen / pclose -FILE *popen(const char *cmd, const char *mode) { - (void)cmd; (void)mode; - errno = ENOSYS; - return NULL; -} - -int pclose(FILE *f) { - (void)f; - errno = ENOSYS; - return -1; -} - -// TODO tmpfile() -FILE *tmpfile(void) { - errno = ENOSYS; - return NULL; -} - - -int fextflags(FILE *f, int extflags) { - int old = f->extflags; - f->extflags = extflags; - return old; -} - -int setvbuf(FILE *restrict f, char *restrict buf, int type, size_t size) { - if (type == _IONBF) { - free(f->readbuf); - f->readbuf = NULL; - return 0; - } else if (type == _IOFBF && buf == NULL) { - (void) size; - f->rblen = 0; - f->rbcap = BUFSIZ; - f->readbuf = malloc(f->rbcap); - return f->readbuf ? 0 : -1; - } else { - return errno = ENOSYS, -1; - } -} - -static void fadvance(long amt, FILE *f) { - bool pos_neg = f->pos < 0; - f->pos += amt; - if (pos_neg && f->pos >= 0) - f->pos = -1; -} - -size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict f) { - size_t total = size*nitems, pos = 0; - unsigned char *buf = ptr; - - if (f->fd < 0) { - errno = EBADF; - return 0; - } - if (size == 0) { - return 0; - } - - while (pos < total) { - long res = 0; - if (f->readbuf) { - if (0 == f->rblen && total - pos < (f->rbcap >> 1)) { - res = _sys_read(f->fd, f->readbuf, f->rbcap, f->pos); - if (res < 0) { - f->error = true; - errno = -res; - break; - } else if (res == 0) { - f->eof = true; - break; - } else { - f->rblen = res; - } - } - if (0 < f->rblen) { - res = MIN(total - pos, f->rblen); - memcpy(buf + pos, f->readbuf, res); - f->rblen -= res; - memmove(f->readbuf, f->readbuf + res, f->rblen); - } - } - if (res == 0) { - /* no cache hit */ - res = _sys_read(f->fd, buf + pos, total - pos, f->pos); - if (res < 0) { - f->error = true; - errno = -res; - break; - } else if (res == 0) { - f->eof = true; - break; - } - } - pos += res; - fadvance(res, f); - if (f->extflags & FEXT_NOFILL) break; - } - return pos == total ? nitems : (pos/size); -} - -size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict f) { - size_t total = size*nitems, pos = 0; - const unsigned char *buf = ptr; - - if (f->fd < 0) { - errno = EBADF; - return 0; - } - if (size == 0) - return 0; - - while (pos < total) { - long res = _sys_write(f->fd, buf + pos, total - pos, f->pos, 0); - if (res < 0) { - f->error = true; - errno = -res; - return pos/size; - } else if (res == 0) { - f->eof = true; - return pos/size; - } else { - pos += res; - fadvance(res, f); - } - } - return nitems; -} - -int fputs(const char *s, FILE *f) { - return fprintf(f, "%s\n", s); -} - -char *fgets(char *buf, int size, FILE *f) { - int pos, c; - for (pos = 0; pos < size-1; ) { - c = fgetc(f); - if (c == EOF) break; - buf[pos++] = c; - if (c == '\n') break; - } - if (pos == 0 || f->error) { - return NULL; - } else { - buf[pos] = '\0'; - return buf; - } -} - -int fgetc(FILE *f) { - char c; - size_t ret = fread(&c, 1, 1, f); - return ret ? c : EOF; -} -int getc(FILE *f) { return fgetc(f); } - -int fputc(int c, FILE *f) { - return fwrite(&c, 1, 1, f) ? c : EOF; -} -int putc(int c, FILE *f) { return fputc(c, f); } - -// TODO ungetc -int ungetc(int c, FILE *f) { - (void)c; (void)f; - __libc_panic("unimplemented"); -} - -int fseek(FILE *f, long offset, int whence) { - return fseeko(f, offset, whence); -} - -int fseeko(FILE *f, off_t offset, int whence) { - if (fflush(f)) - return -1; - - long base; - switch (whence) { - case SEEK_SET: - base = 0; - break; - case SEEK_CUR: - base = f->pos; - // TODO untested - if (f->readbuf) { - base -= f->rblen; - } - break; - case SEEK_END: - base = _sys_getsize(f->fd); - if (base < 0) - base = -1; - break; - default: - errno = EINVAL; - return -1; - } - f->rblen = 0; - - if (base >= 0 && base + offset < 0) { - /* underflow */ - errno = EINVAL; - return -1; - } else if (base < 0 && base + offset >= 0) { - /* overflow - went from a negative offset (relative to EOF) - * to a positive offset (from start of file). - * can only happen when getsize() is unsupported */ - errno = EINVAL; - return -1; - } - f->pos = base + offset; - f->eof = false; - return 0; -} - -long ftell(FILE *f) { - return ftello(f); -} - -off_t ftello(FILE *f) { - return f->pos; -} - -int fclose(FILE *f) { - fflush(f); - if (f->fd > 0) close(f->fd); - free(f->readbuf); - if (f != &_stdin_null && f != &_stdout_null && f != &_stderr_null) - free(f); - return 0; -} - -int fflush(FILE *f) { - (void)f; - return 0; -} - -int feof(FILE *f) { - return f->eof; -} - -int ferror(FILE *f) { - return f->error; -} - -void clearerr(FILE *f) { - f->error = false; - f->eof = false; -} diff --git a/src/user/lib/stdio/file.h b/src/user/lib/stdio/file.h deleted file mode 100644 index 3bd64a1..0000000 --- a/src/user/lib/stdio/file.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include <stdbool.h> -#include <stdio.h> - -struct _LIBC_FILE { - int fd; - long pos; - bool eof; - bool error; - int extflags; - - char *readbuf; - size_t rblen, rbcap; -}; diff --git a/src/user/lib/stdio/misc.c b/src/user/lib/stdio/misc.c deleted file mode 100644 index 45144f3..0000000 --- a/src/user/lib/stdio/misc.c +++ /dev/null @@ -1,52 +0,0 @@ -#include <errno.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -void perror(const char *s) { - if (s) fprintf(stderr, "%s: ", s); - fprintf(stderr, "%s\n", strerror(errno)); -} - -int puts(const char *s) { - return printf("%s\n", s); -} - -int getchar(void) { - return fgetc(stdin); -} - -int putchar(int c) { - return fputc(c, stdout); -} - -off_t lseek(int fd, off_t off, int whence) { - (void)fd; (void)off; (void)whence; - errno = ENOSYS; - return -1; -} - -int remove(const char *path) { - return unlink(path); -} - -// TODO! VFSOP_MOVE -int rename(const char *old, const char *new) { - (void)old; (void)new; - errno = ENOSYS; - return -1; -} - -// TODO tmpnam -char *tmpnam(char *s) { - static char buf[L_tmpnam]; - if (!s) s = buf; - strcpy(s, "/tmp/tmpnam"); - return s; -} - -// TODO sscanf -int sscanf(const char *restrict s, const char *restrict format, ...) { - (void)s; (void)format; - return 0; -} diff --git a/src/user/lib/stdlib.c b/src/user/lib/stdlib.c deleted file mode 100644 index 1739230..0000000 --- a/src/user/lib/stdlib.c +++ /dev/null @@ -1,148 +0,0 @@ -#include <_proc.h> -#include <bits/panic.h> -#include <camellia.h> -#include <camellia/syscalls.h> -#include <ctype.h> -#include <errno.h> -#include <string.h> - -_Noreturn void abort(void) { - _sys_exit(1); -} - -static const char *progname; -const char *getprogname(void) { - return progname; -} -void setprogname(const char *pg) { - progname = pg; - setproctitle(NULL); -} - -void setproctitle(const char *fmt, ...) { - // TODO bounds checking - if (!fmt) { - strcpy(_psdata_loc->desc, progname); - return; - } - sprintf(_psdata_loc->desc, "%s: ", progname); - - va_list argp; - va_start(argp, fmt); - vsnprintf(_psdata_loc->desc + strlen(_psdata_loc->desc), 128, fmt, argp); - va_end(argp); -} - -int mkstemp(char *template) { - // TODO randomize template - hid_t h = camellia_open(template, OPEN_CREATE | OPEN_RW); - if (h < 0) { - errno = -h; - return -1; - } - // TODO truncate - return h; -} - -// TODO process env -char *getenv(const char *name) { - (void)name; - return NULL; -} - -// TODO system() -int system(const char *cmd) { - (void)cmd; - errno = ENOSYS; - return -1; -} - -int abs(int i) { - return i < 0 ? -i : i; -} - -int atoi(const char *s) { - return strtol(s, NULL, 10); -} - -double atof(const char *s) { - (void)s; - __libc_panic("unimplemented"); -} - -static unsigned long long -strton(const char *restrict s, char **restrict end, int base, int *sign) -{ - long res = 0; - - while (isspace(*s)) s++; - - if (sign) *sign = 1; - if (*s == '+') { - s++; - } else if (*s == '-') { - s++; - if (sign) *sign = -1; - } - - if (base == 0) { - if (*s == '0') { - s++; - if (*s == 'x' || *s == 'X') { - s++; - base = 16; - } else { - base = 8; - } - } else { - base = 10; - } - } - - for (;;) { - unsigned char digit = *s; - if ('0' <= digit && digit <= '9') digit -= '0'; - else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10; - else if ('A' <= digit && digit <= 'Z') digit -= 'A' - 10; - else break; - - if (digit >= base) break; - // TODO overflow check - res *= base; - res += digit; - - s++; - } - if (end) *end = (void*)s; - return res; -} - -long strtol(const char *restrict s, char **restrict end, int base) { - int sign; - long n = strton(s, end, base, &sign); - return n * sign; -} - -long long strtoll(const char *restrict s, char **restrict end, int base) { - int sign; - long long n = strton(s, end, base, &sign); - return n * sign; -} - -unsigned long strtoul(const char *restrict s, char **restrict end, int base) { - return strton(s, end, base, NULL); -} - -unsigned long long strtoull(const char *restrict s, char **restrict end, int base) { - return strton(s, end, base, NULL); -} - -double strtod(const char *restrict s, char **restrict end) { - (void)s; (void)end; - __libc_panic("unimplemented"); -} - -void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *a, const void *b)) { - (void)base; (void)nmemb; (void)size; (void)cmp; - __libc_panic("unimplemented"); -} diff --git a/src/user/lib/string/strerror.c b/src/user/lib/string/strerror.c deleted file mode 100644 index c838299..0000000 --- a/src/user/lib/string/strerror.c +++ /dev/null @@ -1,13 +0,0 @@ -#include <string.h> - -static const char *errstr[] = { -# define E(n, str) [n] = str, -# include <__errno.h> -# undef E -}; - -char *strerror(int n) { - if (0 <= n && n * sizeof(*errstr) < sizeof(errstr) && errstr[n]) - return (char*)errstr[n]; - return "unknown error"; -} diff --git a/src/user/lib/string/string.c b/src/user/lib/string/string.c deleted file mode 100644 index c65d7c5..0000000 --- a/src/user/lib/string/string.c +++ /dev/null @@ -1,122 +0,0 @@ -#include <ctype.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> - -char *strchr(const char *s, int c) { - for (; *s || c == 0; s++) { - if (*s == c) return (char *)s; - } - return NULL; -} - -char *strrchr(const char *s, int c) { - for (int i = strlen(s) + 1; i >= 0; i--) { - if (s[i] == c) return (char *)s + i; - } - return NULL; -} - -size_t strspn(const char *s, const char *accept) { - size_t l = 0; - for (; s[l] && strchr(accept, s[l]); l++); - return l; -} - -size_t strcspn(const char *s, const char *reject) { - size_t l = 0; - for (; s[l] && !strchr(reject, s[l]); l++); - return l; -} - -char *strpbrk(const char *s1, const char *s2) { - for (; *s1; s1++) { - if (strchr(s2, *s1)) return (char*)s1; - } - return NULL; -} - -char *strtok(char *restrict s, const char *restrict sep) { - static char *state; - return strtok_r(s, sep, &state); -} - -char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state) { - char *end; - if (!s) s = *state; - s += strspn(s, sep); /* beginning of token */ - if (!*s) return NULL; - - end = s + strcspn(s, sep); - if (*end) { - *end = '\0'; - *state = end + 1; - } else { - *state = end; - } - return s; -} - -int strncmp(const char *s1, const char *s2, size_t n) { - for (size_t i = 0; i < n; i++) { - if (s1[i] < s2[i]) return -1; - if (s1[i] > s2[i]) return 1; - } - return 0; -} - -int strcoll(const char *s1, const char *s2) { - return strcmp(s1, s2); -} - -// TODO implement strstr using Boyer-Moore -char *strstr(const char *s1, const char *s2) { - size_t l1 = strlen(s1), l2 = strlen(s2); - for (; l2 <= l1; s1++, l1--) { - if (memcmp(s1, s2, l2) == 0) return (char*)s1; - } - return NULL; -} - -char *strcpy(char *restrict s1, const char *restrict s2) { - while (*s2) *s1++ = *s2++; - *s1 = *s2; - return s1; -} - -char *strncpy(char *restrict dst, const char *restrict src, size_t n) { - for (size_t i = 0; i < n; i++) { - dst[i] = src[i]; - if (dst[i] == '\0') return dst + i; // TODO fill with null bytes - } - return dst; -} - -char *stpncpy(char *restrict dst, const char *restrict src, size_t n) { - return stpncpy(dst, src, n) + n; -} - -char *strdup(const char *s) { - size_t len = strlen(s) + 1; - char *buf = malloc(len); - if (buf) memcpy(buf, s, len); - return buf; -} - -/* strings.h */ -int strcasecmp(const char *s1, const char *s2) { - return strncasecmp(s1, s2, ~0); -} - -int strncasecmp(const char *s1, const char *s2, size_t n) { - for (size_t i = 0; i < n; i++) { - char c1 = tolower(s1[i]), c2 = tolower(s2[i]); - if (c1 == '\0' || c1 != c2) { - if (c1 < c2) return -1; - else if (c1 > c2) return 1; - else return 0; - } - } - return 0; -} diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c deleted file mode 100644 index 5196683..0000000 --- a/src/user/lib/syscall.c +++ /dev/null @@ -1,104 +0,0 @@ -/* generated by syscall.c.awk - * don't modify manually, instead run: - * make src/user/lib/syscall.c - */ -#include <camellia/syscalls.h> - - -_Noreturn void _sys_exit(long ret) { - _syscall(_SYS_EXIT, ret, 0, 0, 0, 0); - __builtin_unreachable(); -} - -long _sys_await(void) { - return _syscall(_SYS_AWAIT, 0, 0, 0, 0, 0); -} - -long _sys_fork(int flags, hid_t __user *fs_front) { - return _syscall(_SYS_FORK, (long)flags, (long)fs_front, 0, 0, 0); -} - -hid_t _sys_open(const char __user *path, long len, int flags) { - return (hid_t)_syscall(_SYS_OPEN, (long)path, len, (long)flags, 0, 0); -} - -long _sys_mount(hid_t h, const char __user *path, long len) { - return _syscall(_SYS_MOUNT, (long)h, (long)path, len, 0, 0); -} - -hid_t _sys_dup(hid_t from, hid_t to, int flags) { - return (hid_t)_syscall(_SYS_DUP, (long)from, (long)to, (long)flags, 0, 0); -} - -long _sys_read(hid_t h, void __user *buf, size_t len, long offset) { - return _syscall(_SYS_READ, (long)h, (long)buf, (long)len, offset, 0); -} - -long _sys_write(hid_t h, const void __user *buf, size_t len, long offset, int flags) { - return _syscall(_SYS_WRITE, (long)h, (long)buf, (long)len, offset, (long)flags); -} - -long _sys_getsize(hid_t h) { - return _syscall(_SYS_GETSIZE, (long)h, 0, 0, 0, 0); -} - -long _sys_remove(hid_t h) { - return _syscall(_SYS_REMOVE, (long)h, 0, 0, 0, 0); -} - -long _sys_close(hid_t h) { - return _syscall(_SYS_CLOSE, (long)h, 0, 0, 0, 0); -} - -hid_t _sys_fs_wait(char __user *buf, long max_len, struct ufs_request __user *res) { - return (hid_t)_syscall(_SYS_FS_WAIT, (long)buf, max_len, (long)res, 0, 0); -} - -long _sys_fs_respond(hid_t hid, const void __user *buf, long ret, int flags) { - return _syscall(_SYS_FS_RESPOND, (long)hid, (long)buf, ret, (long)flags, 0); -} - -void __user *_sys_memflag(void __user *addr, size_t len, int flags) { - return (void __user *)_syscall(_SYS_MEMFLAG, (long)addr, (long)len, (long)flags, 0, 0); -} - -long _sys_pipe(hid_t __user user_ends[2], int flags) { - return _syscall(_SYS_PIPE, (long)user_ends, (long)flags, 0, 0, 0); -} - -void _sys_sleep(long ms) { - return (void)_syscall(_SYS_SLEEP, ms, 0, 0, 0, 0); -} - -void _sys_filicide(void) { - return (void)_syscall(_SYS_FILICIDE, 0, 0, 0, 0, 0); -} - -void _sys_intr(void) { - return (void)_syscall(_SYS_INTR, 0, 0, 0, 0, 0); -} - -void _sys_intr_set(void __user *ip) { - return (void)_syscall(_SYS_INTR_SET, (long)ip, 0, 0, 0, 0); -} - -uint32_t _sys_getpid(void) { - return (uint32_t)_syscall(_SYS_GETPID, 0, 0, 0, 0, 0); -} - -uint32_t _sys_getppid(void) { - return (uint32_t)_syscall(_SYS_GETPPID, 0, 0, 0, 0, 0); -} - -int _sys_wait2(int pid, int flags, struct sys_wait2 __user *out) { - return (int)_syscall(_SYS_WAIT2, (long)pid, (long)flags, (long)out, 0, 0); -} - -long _sys_execbuf(void __user *buf, size_t len) { - return _syscall(_SYS_EXECBUF, (long)buf, (long)len, 0, 0, 0); -} - -void _sys_debug_klog(const void __user *buf, size_t len) { - return (void)_syscall(_SYS_DEBUG_KLOG, (long)buf, (long)len, 0, 0, 0); -} - diff --git a/src/user/lib/syscall.c.awk b/src/user/lib/syscall.c.awk deleted file mode 100644 index 591a6f0..0000000 --- a/src/user/lib/syscall.c.awk +++ /dev/null @@ -1,51 +0,0 @@ -BEGIN { - print "\ -/* generated by syscall.c.awk\n\ - * don't modify manually, instead run:\n\ - * make src/user/lib/syscall.c\n\ - */\n\ -#include <camellia/syscalls.h>\n\ -\n"; -} - -/_syscall\(/ { next; } # skipping _syscall(), it's implemented elsewhere - -/\);/ { - sub(/;/, " {"); - print $0; - - name = substr($0, match($0, /_sys_[^(]+/), RLENGTH); - rets = substr($0, 0, RSTART - 1); - sub(/ *$/, "", rets) - - params = substr($0, match($0, /\(.+\)/) + 1, RLENGTH - 2); - gsub(/\[[^\]]\]/, "", params); - if (params == "void") params = "" - - split(params, p, /,/); - for (i = 0; i < 6; i += 1) { - if (p[i]) { - # p[i] is a parameter, convert it into an expression to pass to _syscall() - sub(/^ */, "", p[i]); # strip - split(p[i], words, / /); - if (length(words) != 1) { - var = words[length(words)]; - sub(/\*/, "", var); - if (words[1] != "long") var = "(long)" var; - } - p[i] = var; - } else { - p[i] = 0; - } - } - - printf "\t"; - if (!index($0, "_Noreturn")) { - printf "return "; - if (rets != "long") printf "(%s)", rets; - } - printf "_syscall(%s, %s, %s, %s, %s, %s);\n", toupper(name), p[1], p[2], p[3], p[4], p[5]; - if (index($0, "_Noreturn")) print "\t__builtin_unreachable();"; - - print "}\n"; -} diff --git a/src/user/lib/syscall.s b/src/user/lib/syscall.s deleted file mode 100644 index 07f5631..0000000 --- a/src/user/lib/syscall.s +++ /dev/null @@ -1,9 +0,0 @@ -.section .text -.global _syscall -.type _syscall, @function -_syscall: - push %r10 - mov %rcx, %r10 - syscall - pop %r10 - ret diff --git a/src/user/lib/sysstat.c b/src/user/lib/sysstat.c deleted file mode 100644 index 97bb50b..0000000 --- a/src/user/lib/sysstat.c +++ /dev/null @@ -1,50 +0,0 @@ -#include <camellia.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -int fstat(int fd, struct stat *sb) { - (void)fd; - memset(sb, 0, sizeof sb); - // TODO save info if it was a dir - sb->st_mode = 0777 | S_IFREG; - return 0; -} - -int stat(const char *restrict path, struct stat *restrict sb) { - int fd, ret; - fd = camellia_open(path, OPEN_READ); - if (fd < 0) { - return -1; - } - ret = fstat(fd, sb); - close(fd); - return ret; -} - -int lstat(const char *restrict path, struct stat *restrict sb) { - // TODO assumes no symlink support - return stat(path, sb); -} - -int mkdir(const char *path, mode_t mode) { - // TODO error when directory already exits - // TODO fopen-like wrapper that calls open() with a processed path - (void)mode; - size_t plen = strlen(path); - char *tmp = NULL; - /* ensure trailing slash */ - if (plen >= 1 && path[plen - 1] != '/') { - tmp = malloc(plen + 2); - memcpy(tmp, path, plen); - tmp[plen] = '/'; - tmp[plen + 1] = '\0'; - path = tmp; - } - FILE *f = fopen(path, "a"); /* sets errno */ - if (f) fclose(f); - free(tmp); - return f == NULL ? -1 : 0; -} diff --git a/src/user/lib/syswait.c b/src/user/lib/syswait.c deleted file mode 100644 index ce80c7b..0000000 --- a/src/user/lib/syswait.c +++ /dev/null @@ -1,22 +0,0 @@ -#include <bits/panic.h> -#include <camellia.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <sys/resource.h> -#include <sys/wait.h> - -pid_t wait3(int *wstatus, int opts, struct rusage *rusage) { - struct sys_wait2 res; - if (opts || rusage) { - __libc_panic("unimplemented"); - } - pid_t ret = _sys_wait2(-1, 0, &res); - if (ret < 0) { - errno = -ret; - return -1; - } - if (wstatus) { - *wstatus = res.status & 0xFF; - } - return ret; -} diff --git a/src/user/lib/thread.S b/src/user/lib/thread.S deleted file mode 100644 index 2900544..0000000 --- a/src/user/lib/thread.S +++ /dev/null @@ -1,40 +0,0 @@ -#define ASM_FILE 1 -#include <camellia/syscalls.h> -#include <camellia/flags.h> - -.section .text -.global thread_creates -.type thread_creates, @function -// void thread_creates(int flags, void (*fn)(void*), void *arg, void *stack); -thread_creates: - push %r12 - push %r13 - push %r14 - - /* save fn, arg, stack */ - mov %rsi, %r12 - mov %rdx, %r13 - mov %rcx, %r14 - - mov %rdi, %rsi - or $(FORK_SHAREMEM | FORK_SHAREHANDLE), %rsi - mov $_SYS_FORK, %rdi - xor %rdx, %rdx - syscall - - test %rax, %rax - jz 1f - /* in parent, return normally */ - pop %r14 - pop %r13 - pop %r12 - ret -1: /* in child */ - mov %r14, %rsp - mov %r13, %rdi - call *%r12 - - mov $_SYS_EXIT, %rdi - xor %rsi, %rsi - syscall - hlt /* if all else fails... */ diff --git a/src/user/lib/time.c b/src/user/lib/time.c deleted file mode 100644 index 4709ecd..0000000 --- a/src/user/lib/time.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <errno.h> -#include <time.h> - -// TODO time -time_t time(time_t *tloc) { - time_t ret = 0; - if (tloc) *tloc = ret; - return ret; -} - -clock_t clock(void) { - return 0; -} - -struct tm *gmtime(const time_t *timer) { - (void)timer; - errno = ENOSYS; - return NULL; -} - -struct tm *localtime(const time_t *timer) { - (void)timer; - errno = ENOSYS; - return NULL; -} - -time_t mktime(struct tm *timeptr) { - (void)timeptr; - return 0; -} - -double difftime(time_t time1, time_t time0) { - (void)time1; (void)time0; - return 0; -} - -size_t strftime( - char *restrict s, size_t maxsize, - const char *restrict format, const struct tm *restrict timeptr) -{ - (void)s; (void)maxsize; (void)format; (void)timeptr; - return 0; -} diff --git a/src/user/lib/unistd.c b/src/user/lib/unistd.c deleted file mode 100644 index 578cadc..0000000 --- a/src/user/lib/unistd.c +++ /dev/null @@ -1,267 +0,0 @@ -#include <bits/panic.h> -#include <camellia.h> -#include <camellia/path.h> -#include <camellia/syscalls.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <elfload.h> - -int errno = 0; -static char *_environ[] = {NULL}; -char **environ = _environ; - -int fork(void) { - return _sys_fork(0, NULL); -} - -pid_t vfork(void) { - // TODO vfork is implemented improperly and will break stuff - return _sys_fork(0, NULL); -} - -int close(hid_t h) { - return _sys_close(h); -} - -_Noreturn void exit(int c) { - _sys_exit(c); -} -_Noreturn void _exit(int c) { exit(c); }; - -ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize) { - (void)path; (void)buf; (void)bufsize; - errno = ENOSYS; - return -1; -} - -int link(const char *path1, const char *path2) { - (void)path1; (void)path2; - errno = ENOSYS; - return -1; -} - -int unlink(const char *path) { - hid_t h = camellia_open(path, OPEN_WRITE); - if (h < 0) return errno = -h, -1; - long ret = _sys_remove(h); - if (ret < 0) return errno = -ret, -1; - return 0; -} - -int symlink(const char *path1, const char *path2) { - (void)path1; (void)path2; - errno = ENOSYS; - return -1; -} - -// TODO isatty -int isatty(int fd) { - return fd <= 2 ? 1 : 0; -} - - -int execv(const char *path, char *const argv[]) { - return execve(path, argv, NULL); -} - -int execve(const char *path, char *const argv[], char *const envp[]) { - FILE *file = fopen(path, "e"); - char hdr[4] = {0}; - if (!file) - return -1; - - fread(hdr, 1, 4, file); - fseek(file, 0, SEEK_SET); - - if (!memcmp("\x7f""ELF", hdr, 4)) { - elf_execf(file, (void*)argv, (void*)envp); - fclose(file); - } else if (!memcmp("#!", hdr, 2)) { - char buf[256]; - fseek(file, 2, SEEK_SET); - if (fgets(buf, sizeof buf, file)) { - const char *argv [] = {buf, path, NULL}; - char *endl = strchr(buf, '\n'); - if (endl) *endl = '\0'; - execve(buf, (void*)argv, envp); - } - } - - errno = EINVAL; - return -1; -} - - -static const char *__initialcwd; -static char *cwd = NULL, *cwd2 = NULL; -static size_t cwdcapacity = 0; - -static const char *getrealcwd(void) { - /* __initialcwd can't just be initialized with "/" because ld has seemingly - * started to revolt against humanity and not process half the relocations - * it sees. */ - if (cwd) return cwd; - if (__initialcwd) return __initialcwd; - return "/"; -} - -int chdir(const char *path) { - hid_t h; - char *tmp; - size_t len = absolutepath(NULL, path, 0) + 1; /* +1 for the trailing slash */ - if (cwdcapacity < len) { - cwdcapacity = len; - if (cwd) { - cwd = realloc(cwd, len); - cwd2 = realloc(cwd2, len); - } else { - size_t initlen = strlen(__initialcwd) + 1; - if (len < initlen) - len = initlen; - cwd = malloc(initlen); - cwd2 = malloc(initlen); - memcpy(cwd, __initialcwd, initlen); - } - } - absolutepath(cwd2, path, cwdcapacity); - len = strlen(cwd2); - if (cwd2[len - 1] != '/') { - cwd2[len] = '/'; - cwd2[len + 1] = '\0'; - } - - /* check if exists */ - h = camellia_open(cwd2, OPEN_READ); - if (h < 0) return errno = ENOENT, -1; - close(h); - - tmp = cwd; - cwd = cwd2; - cwd2 = tmp; - return 0; -} - -char *getcwd(char *buf, size_t capacity) { - const char *realcwd = getrealcwd(); - size_t len = strlen(realcwd) + 1; - if (capacity < len) { - errno = capacity == 0 ? EINVAL : ERANGE; - return NULL; - } - memcpy(buf, realcwd, len); - return buf; -} - -uid_t getuid(void) { return 0; } -uid_t geteuid(void) { return 0; } -gid_t getgid(void) { return 0; } -gid_t getegid(void) { return 0; } - -int chown(const char *path, uid_t owner, gid_t group) { - (void)path; (void)owner; (void)group; - errno = ENOSYS; - return -1; -} - -int setpgid(pid_t pid, pid_t pgid) { - (void)pid; (void)pgid; - return errno = ENOSYS, -1; -} - -pid_t tcgetpgrp(int fd) { - (void)fd; - return errno = ENOSYS, -1; -} - -int tcsetpgrp(int fd, pid_t pgrp) { - (void)fd; (void)pgrp; - return errno = ENOSYS, -1; -} - -pid_t getpgrp(void) { - __libc_panic("unimplemented"); -} - -pid_t getpid(void) { - return _sys_getpid(); -} - -pid_t getppid(void) { - return _sys_getppid(); -} - -int getgroups(int size, gid_t list[]) { - (void)size; (void)list; - __libc_panic("unimplemented"); -} - -ssize_t read(int fd, void *buf, size_t count) { - // TODO real file descriptor emulation - store offsets - return _sys_read(fd, buf, count, -1); -} - -ssize_t write(int fd, const void *buf, size_t count) { - // TODO real file descriptor emulation - store offsets - return _sys_write(fd, buf, count, -1, 0); -} - -int pipe(int pipefd[2]) { - (void)pipefd; - __libc_panic("unimplemented"); -} - -int dup2(int oldfd, int newfd) { - (void)oldfd; (void)newfd; - __libc_panic("unimplemented"); -} - -size_t absolutepath(char *out, const char *in, size_t size) { - const char *realcwd = getrealcwd(); - size_t len, pos = 0; - if (!in) return strlen(realcwd) + 1; - - if (!(in[0] == '/')) { - len = strlen(realcwd); - if (pos + len <= size && out != realcwd) - memcpy(out + pos, realcwd, len); - pos += len; - - if (realcwd[len - 1] != '/') { - if (pos + 1 <= size) out[pos] = '/'; - pos++; - } - } - - len = strlen(in); - if (pos + len <= size) - memcpy(out + pos, in, len); - pos += len; - - if (pos <= size) { - pos = path_simplify(out, out, pos); - if (pos == 0) return 0; - } - - if (pos + 1 <= size) out[pos] = '\0'; - pos++; - - return pos; -} - -void __setinitialcwd(const char *s) { - __initialcwd = s; -} - -static void intr_null(void) { } - -extern void (*volatile _intr)(void); -void intr_set(void (*fn)(void)) { - _intr = fn ? fn : intr_null; -} - -void intr_default(void) { - exit(-1); -} diff --git a/src/user/lib/vendor/dlmalloc/malloc.c b/src/user/lib/vendor/dlmalloc/malloc.c deleted file mode 100644 index 649cfbc..0000000 --- a/src/user/lib/vendor/dlmalloc/malloc.c +++ /dev/null @@ -1,6280 +0,0 @@ -/* - This is a version (aka dlmalloc) of malloc/free/realloc written by - Doug Lea and released to the public domain, as explained at - http://creativecommons.org/publicdomain/zero/1.0/ Send questions, - comments, complaints, performance data, etc to [email protected] - -* Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea - Note: There may be an updated version of this malloc obtainable at - ftp://gee.cs.oswego.edu/pub/misc/malloc.c - Check before installing! - -* Quickstart - - This library is all in one file to simplify the most common usage: - ftp it, compile it (-O3), and link it into another program. All of - the compile-time options default to reasonable values for use on - most platforms. You might later want to step through various - compile-time and dynamic tuning options. - - For convenience, an include file for code using this malloc is at: - ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.6.h - You don't really need this .h file unless you call functions not - defined in your system include files. The .h file contains only the - excerpts from this file needed for using this malloc on ANSI C/C++ - systems, so long as you haven't changed compile-time options about - naming and tuning parameters. If you do, then you can create your - own malloc.h that does include all settings by cutting at the point - indicated below. Note that you may already by default be using a C - library containing a malloc that is based on some version of this - malloc (for example in linux). You might still want to use the one - in this file to customize settings or to avoid overheads associated - with library versions. - -* Vital statistics: - - Supported pointer/size_t representation: 4 or 8 bytes - size_t MUST be an unsigned type of the same width as - pointers. (If you are using an ancient system that declares - size_t as a signed type, or need it to be a different width - than pointers, you can use a previous release of this malloc - (e.g. 2.7.2) supporting these.) - - Alignment: 8 bytes (minimum) - This suffices for nearly all current machines and C compilers. - However, you can define MALLOC_ALIGNMENT to be wider than this - if necessary (up to 128bytes), at the expense of using more space. - - Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) - 8 or 16 bytes (if 8byte sizes) - Each malloced chunk has a hidden word of overhead holding size - and status information, and additional cross-check word - if FOOTERS is defined. - - Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) - 8-byte ptrs: 32 bytes (including overhead) - - Even a request for zero bytes (i.e., malloc(0)) returns a - pointer to something of the minimum allocatable size. - The maximum overhead wastage (i.e., number of extra bytes - allocated than were requested in malloc) is less than or equal - to the minimum size, except for requests >= mmap_threshold that - are serviced via mmap(), where the worst case wastage is about - 32 bytes plus the remainder from a system page (the minimal - mmap unit); typically 4096 or 8192 bytes. - - Security: static-safe; optionally more or less - The "security" of malloc refers to the ability of malicious - code to accentuate the effects of errors (for example, freeing - space that is not currently malloc'ed or overwriting past the - ends of chunks) in code that calls malloc. This malloc - guarantees not to modify any memory locations below the base of - heap, i.e., static variables, even in the presence of usage - errors. The routines additionally detect most improper frees - and reallocs. All this holds as long as the static bookkeeping - for malloc itself is not corrupted by some other means. This - is only one aspect of security -- these checks do not, and - cannot, detect all possible programming errors. - - If FOOTERS is defined nonzero, then each allocated chunk - carries an additional check word to verify that it was malloced - from its space. These check words are the same within each - execution of a program using malloc, but differ across - executions, so externally crafted fake chunks cannot be - freed. This improves security by rejecting frees/reallocs that - could corrupt heap memory, in addition to the checks preventing - writes to statics that are always on. This may further improve - security at the expense of time and space overhead. (Note that - FOOTERS may also be worth using with MSPACES.) - - By default detected errors cause the program to abort (calling - "abort()"). You can override this to instead proceed past - errors by defining PROCEED_ON_ERROR. In this case, a bad free - has no effect, and a malloc that encounters a bad address - caused by user overwrites will ignore the bad address by - dropping pointers and indices to all known memory. This may - be appropriate for programs that should continue if at all - possible in the face of programming errors, although they may - run out of memory because dropped memory is never reclaimed. - - If you don't like either of these options, you can define - CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything - else. And if if you are sure that your program using malloc has - no errors or vulnerabilities, you can define INSECURE to 1, - which might (or might not) provide a small performance improvement. - - It is also possible to limit the maximum total allocatable - space, using malloc_set_footprint_limit. This is not - designed as a security feature in itself (calls to set limits - are not screened or privileged), but may be useful as one - aspect of a secure implementation. - - Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero - When USE_LOCKS is defined, each public call to malloc, free, - etc is surrounded with a lock. By default, this uses a plain - pthread mutex, win32 critical section, or a spin-lock if if - available for the platform and not disabled by setting - USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined, - recursive versions are used instead (which are not required for - base functionality but may be needed in layered extensions). - Using a global lock is not especially fast, and can be a major - bottleneck. It is designed only to provide minimal protection - in concurrent environments, and to provide a basis for - extensions. If you are using malloc in a concurrent program, - consider instead using nedmalloc - (http://www.nedprod.com/programs/portable/nedmalloc/) or - ptmalloc (See http://www.malloc.de), which are derived from - versions of this malloc. - - System requirements: Any combination of MORECORE and/or MMAP/MUNMAP - This malloc can use unix sbrk or any emulation (invoked using - the CALL_MORECORE macro) and/or mmap/munmap or any emulation - (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system - memory. On most unix systems, it tends to work best if both - MORECORE and MMAP are enabled. On Win32, it uses emulations - based on VirtualAlloc. It also uses common C library functions - like memset. - - Compliance: I believe it is compliant with the Single Unix Specification - (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably - others as well. - -* Overview of algorithms - - This is not the fastest, most space-conserving, most portable, or - most tunable malloc ever written. However it is among the fastest - while also being among the most space-conserving, portable and - tunable. Consistent balance across these factors results in a good - general-purpose allocator for malloc-intensive programs. - - In most ways, this malloc is a best-fit allocator. Generally, it - chooses the best-fitting existing chunk for a request, with ties - broken in approximately least-recently-used order. (This strategy - normally maintains low fragmentation.) However, for requests less - than 256bytes, it deviates from best-fit when there is not an - exactly fitting available chunk by preferring to use space adjacent - to that used for the previous small request, as well as by breaking - ties in approximately most-recently-used order. (These enhance - locality of series of small allocations.) And for very large requests - (>= 256Kb by default), it relies on system memory mapping - facilities, if supported. (This helps avoid carrying around and - possibly fragmenting memory used only for large chunks.) - - All operations (except malloc_stats and mallinfo) have execution - times that are bounded by a constant factor of the number of bits in - a size_t, not counting any clearing in calloc or copying in realloc, - or actions surrounding MORECORE and MMAP that have times - proportional to the number of non-contiguous regions returned by - system allocation routines, which is often just 1. In real-time - applications, you can optionally suppress segment traversals using - NO_SEGMENT_TRAVERSAL, which assures bounded execution even when - system allocators return non-contiguous spaces, at the typical - expense of carrying around more memory and increased fragmentation. - - The implementation is not very modular and seriously overuses - macros. Perhaps someday all C compilers will do as good a job - inlining modular code as can now be done by brute-force expansion, - but now, enough of them seem not to. - - Some compilers issue a lot of warnings about code that is - dead/unreachable only on some platforms, and also about intentional - uses of negation on unsigned types. All known cases of each can be - ignored. - - For a longer but out of date high-level description, see - http://gee.cs.oswego.edu/dl/html/malloc.html - -* MSPACES - If MSPACES is defined, then in addition to malloc, free, etc., - this file also defines mspace_malloc, mspace_free, etc. These - are versions of malloc routines that take an "mspace" argument - obtained using create_mspace, to control all internal bookkeeping. - If ONLY_MSPACES is defined, only these versions are compiled. - So if you would like to use this allocator for only some allocations, - and your system malloc for others, you can compile with - ONLY_MSPACES and then do something like... - static mspace mymspace = create_mspace(0,0); // for example - #define mymalloc(bytes) mspace_malloc(mymspace, bytes) - - (Note: If you only need one instance of an mspace, you can instead - use "USE_DL_PREFIX" to relabel the global malloc.) - - You can similarly create thread-local allocators by storing - mspaces as thread-locals. For example: - static __thread mspace tlms = 0; - void* tlmalloc(size_t bytes) { - if (tlms == 0) tlms = create_mspace(0, 0); - return mspace_malloc(tlms, bytes); - } - void tlfree(void* mem) { mspace_free(tlms, mem); } - - Unless FOOTERS is defined, each mspace is completely independent. - You cannot allocate from one and free to another (although - conformance is only weakly checked, so usage errors are not always - caught). If FOOTERS is defined, then each chunk carries around a tag - indicating its originating mspace, and frees are directed to their - originating spaces. Normally, this requires use of locks. - - ------------------------- Compile-time options --------------------------- - -Be careful in setting #define values for numerical constants of type -size_t. On some systems, literal values are not automatically extended -to size_t precision unless they are explicitly casted. You can also -use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. - -WIN32 default: defined if _WIN32 defined - Defining WIN32 sets up defaults for MS environment and compilers. - Otherwise defaults are for unix. Beware that there seem to be some - cases where this malloc might not be a pure drop-in replacement for - Win32 malloc: Random-looking failures from Win32 GDI API's (eg; - SetDIBits()) may be due to bugs in some video driver implementations - when pixel buffers are malloc()ed, and the region spans more than - one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) - default granularity, pixel buffers may straddle virtual allocation - regions more often than when using the Microsoft allocator. You can - avoid this by using VirtualAlloc() and VirtualFree() for all pixel - buffers rather than using malloc(). If this is not possible, - recompile this malloc with a larger DEFAULT_GRANULARITY. Note: - in cases where MSC and gcc (cygwin) are known to differ on WIN32, - conditions use _MSC_VER to distinguish them. - -DLMALLOC_EXPORT default: extern - Defines how public APIs are declared. If you want to export via a - Windows DLL, you might define this as - #define DLMALLOC_EXPORT extern __declspec(dllexport) - If you want a POSIX ELF shared object, you might use - #define DLMALLOC_EXPORT extern __attribute__((visibility("default"))) - -MALLOC_ALIGNMENT default: (size_t)(2 * sizeof(void *)) - Controls the minimum alignment for malloc'ed chunks. It must be a - power of two and at least 8, even on machines for which smaller - alignments would suffice. It may be defined as larger than this - though. Note however that code and data structures are optimized for - the case of 8-byte alignment. - -MSPACES default: 0 (false) - If true, compile in support for independent allocation spaces. - This is only supported if HAVE_MMAP is true. - -ONLY_MSPACES default: 0 (false) - If true, only compile in mspace versions, not regular versions. - -USE_LOCKS default: 0 (false) - Causes each call to each public routine to be surrounded with - pthread or WIN32 mutex lock/unlock. (If set true, this can be - overridden on a per-mspace basis for mspace versions.) If set to a - non-zero value other than 1, locks are used, but their - implementation is left out, so lock functions must be supplied manually, - as described below. - -USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available - If true, uses custom spin locks for locking. This is currently - supported only gcc >= 4.1, older gccs on x86 platforms, and recent - MS compilers. Otherwise, posix locks or win32 critical sections are - used. - -USE_RECURSIVE_LOCKS default: not defined - If defined nonzero, uses recursive (aka reentrant) locks, otherwise - uses plain mutexes. This is not required for malloc proper, but may - be needed for layered allocators such as nedmalloc. - -LOCK_AT_FORK default: not defined - If defined nonzero, performs pthread_atfork upon initialization - to initialize child lock while holding parent lock. The implementation - assumes that pthread locks (not custom locks) are being used. In other - cases, you may need to customize the implementation. - -FOOTERS default: 0 - If true, provide extra checking and dispatching by placing - information in the footers of allocated chunks. This adds - space and time overhead. - -INSECURE default: 0 - If true, omit checks for usage errors and heap space overwrites. - -USE_DL_PREFIX default: NOT defined - Causes compiler to prefix all public routines with the string 'dl'. - This can be useful when you only want to use this malloc in one part - of a program, using your regular system malloc elsewhere. - -MALLOC_INSPECT_ALL default: NOT defined - If defined, compiles malloc_inspect_all and mspace_inspect_all, that - perform traversal of all heap space. Unless access to these - functions is otherwise restricted, you probably do not want to - include them in secure implementations. - -ABORT default: defined as abort() - Defines how to abort on failed checks. On most systems, a failed - check cannot die with an "assert" or even print an informative - message, because the underlying print routines in turn call malloc, - which will fail again. Generally, the best policy is to simply call - abort(). It's not very useful to do more than this because many - errors due to overwriting will show up as address faults (null, odd - addresses etc) rather than malloc-triggered checks, so will also - abort. Also, most compilers know that abort() does not return, so - can better optimize code conditionally calling it. - -PROCEED_ON_ERROR default: defined as 0 (false) - Controls whether detected bad addresses cause them to bypassed - rather than aborting. If set, detected bad arguments to free and - realloc are ignored. And all bookkeeping information is zeroed out - upon a detected overwrite of freed heap space, thus losing the - ability to ever return it from malloc again, but enabling the - application to proceed. If PROCEED_ON_ERROR is defined, the - static variable malloc_corruption_error_count is compiled in - and can be examined to see if errors have occurred. This option - generates slower code than the default abort policy. - -DEBUG default: NOT defined - The DEBUG setting is mainly intended for people trying to modify - this code or diagnose problems when porting to new platforms. - However, it may also be able to better isolate user errors than just - using runtime checks. The assertions in the check routines spell - out in more detail the assumptions and invariants underlying the - algorithms. The checking is fairly extensive, and will slow down - execution noticeably. Calling malloc_stats or mallinfo with DEBUG - set will attempt to check every non-mmapped allocated and free chunk - in the course of computing the summaries. - -ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) - Debugging assertion failures can be nearly impossible if your - version of the assert macro causes malloc to be called, which will - lead to a cascade of further failures, blowing the runtime stack. - ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), - which will usually make debugging easier. - -MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 - The action to take before "return 0" when malloc fails to be able to - return memory because there is none available. - -HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES - True if this system supports sbrk or an emulation of it. - -MORECORE default: sbrk - The name of the sbrk-style system routine to call to obtain more - memory. See below for guidance on writing custom MORECORE - functions. The type of the argument to sbrk/MORECORE varies across - systems. It cannot be size_t, because it supports negative - arguments, so it is normally the signed type of the same width as - size_t (sometimes declared as "intptr_t"). It doesn't much matter - though. Internally, we only call it with arguments less than half - the max value of a size_t, which should work across all reasonable - possibilities, although sometimes generating compiler warnings. - -MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE - If true, take advantage of fact that consecutive calls to MORECORE - with positive arguments always return contiguous increasing - addresses. This is true of unix sbrk. It does not hurt too much to - set it true anyway, since malloc copes with non-contiguities. - Setting it false when definitely non-contiguous saves time - and possibly wasted space it would take to discover this though. - -MORECORE_CANNOT_TRIM default: NOT defined - True if MORECORE cannot release space back to the system when given - negative arguments. This is generally necessary only if you are - using a hand-crafted MORECORE function that cannot handle negative - arguments. - -NO_SEGMENT_TRAVERSAL default: 0 - If non-zero, suppresses traversals of memory segments - returned by either MORECORE or CALL_MMAP. This disables - merging of segments that are contiguous, and selectively - releasing them to the OS if unused, but bounds execution times. - -HAVE_MMAP default: 1 (true) - True if this system supports mmap or an emulation of it. If so, and - HAVE_MORECORE is not true, MMAP is used for all system - allocation. If set and HAVE_MORECORE is true as well, MMAP is - primarily used to directly allocate very large blocks. It is also - used as a backup strategy in cases where MORECORE fails to provide - space from system. Note: A single call to MUNMAP is assumed to be - able to unmap memory that may have be allocated using multiple calls - to MMAP, so long as they are adjacent. - -HAVE_MREMAP default: 1 on linux, else 0 - If true realloc() uses mremap() to re-allocate large blocks and - extend or shrink allocation spaces. - -MMAP_CLEARS default: 1 except on WINCE. - True if mmap clears memory so calloc doesn't need to. This is true - for standard unix mmap using /dev/zero and on WIN32 except for WINCE. - -USE_BUILTIN_FFS default: 0 (i.e., not used) - Causes malloc to use the builtin ffs() function to compute indices. - Some compilers may recognize and intrinsify ffs to be faster than the - supplied C version. Also, the case of x86 using gcc is special-cased - to an asm instruction, so is already as fast as it can be, and so - this setting has no effect. Similarly for Win32 under recent MS compilers. - (On most x86s, the asm version is only slightly faster than the C version.) - -malloc_getpagesize default: derive from system includes, or 4096. - The system page size. To the extent possible, this malloc manages - memory from the system in page-size units. This may be (and - usually is) a function rather than a constant. This is ignored - if WIN32, where page size is determined using getSystemInfo during - initialization. - -USE_DEV_RANDOM default: 0 (i.e., not used) - Causes malloc to use /dev/random to initialize secure magic seed for - stamping footers. Otherwise, the current time is used. - -NO_MALLINFO default: 0 - If defined, don't compile "mallinfo". This can be a simple way - of dealing with mismatches between system declarations and - those in this file. - -MALLINFO_FIELD_TYPE default: size_t - The type of the fields in the mallinfo struct. This was originally - defined as "int" in SVID etc, but is more usefully defined as - size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set - -NO_MALLOC_STATS default: 0 - If defined, don't compile "malloc_stats". This avoids calls to - fprintf and bringing in stdio dependencies you might not want. - -REALLOC_ZERO_BYTES_FREES default: not defined - This should be set if a call to realloc with zero bytes should - be the same as a call to free. Some people think it should. Otherwise, - since this malloc returns a unique pointer for malloc(0), so does - realloc(p, 0). - -LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H -LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H -LACKS_STDLIB_H LACKS_SCHED_H LACKS_TIME_H default: NOT defined unless on WIN32 - Define these if your system does not have these header files. - You might need to manually insert some of the declarations they provide. - -DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, - system_info.dwAllocationGranularity in WIN32, - otherwise 64K. - Also settable using mallopt(M_GRANULARITY, x) - The unit for allocating and deallocating memory from the system. On - most systems with contiguous MORECORE, there is no reason to - make this more than a page. However, systems with MMAP tend to - either require or encourage larger granularities. You can increase - this value to prevent system allocation functions to be called so - often, especially if they are slow. The value must be at least one - page and must be a power of two. Setting to 0 causes initialization - to either page size or win32 region size. (Note: In previous - versions of malloc, the equivalent of this option was called - "TOP_PAD") - -DEFAULT_TRIM_THRESHOLD default: 2MB - Also settable using mallopt(M_TRIM_THRESHOLD, x) - The maximum amount of unused top-most memory to keep before - releasing via malloc_trim in free(). Automatic trimming is mainly - useful in long-lived programs using contiguous MORECORE. Because - trimming via sbrk can be slow on some systems, and can sometimes be - wasteful (in cases where programs immediately afterward allocate - more large chunks) the value should be high enough so that your - overall system performance would improve by releasing this much - memory. As a rough guide, you might set to a value close to the - average size of a process (program) running on your system. - Releasing this much memory would allow such a process to run in - memory. Generally, it is worth tuning trim thresholds when a - program undergoes phases where several large chunks are allocated - and released in ways that can reuse each other's storage, perhaps - mixed with phases where there are no such chunks at all. The trim - value must be greater than page size to have any useful effect. To - disable trimming completely, you can set to MAX_SIZE_T. Note that the trick - some people use of mallocing a huge space and then freeing it at - program startup, in an attempt to reserve system memory, doesn't - have the intended effect under automatic trimming, since that memory - will immediately be returned to the system. - -DEFAULT_MMAP_THRESHOLD default: 256K - Also settable using mallopt(M_MMAP_THRESHOLD, x) - The request size threshold for using MMAP to directly service a - request. Requests of at least this size that cannot be allocated - using already-existing space will be serviced via mmap. (If enough - normal freed space already exists it is used instead.) Using mmap - segregates relatively large chunks of memory so that they can be - individually obtained and released from the host system. A request - serviced through mmap is never reused by any other request (at least - not directly; the system may just so happen to remap successive - requests to the same locations). Segregating space in this way has - the benefits that: Mmapped space can always be individually released - back to the system, which helps keep the system level memory demands - of a long-lived program low. Also, mapped memory doesn't become - `locked' between other chunks, as can happen with normally allocated - chunks, which means that even trimming via malloc_trim would not - release them. However, it has the disadvantage that the space - cannot be reclaimed, consolidated, and then used to service later - requests, as happens with normal chunks. The advantages of mmap - nearly always outweigh disadvantages for "large" chunks, but the - value of "large" may vary across systems. The default is an - empirically derived value that works well in most systems. You can - disable mmap by setting to MAX_SIZE_T. - -MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP - The number of consolidated frees between checks to release - unused segments when freeing. When using non-contiguous segments, - especially with multiple mspaces, checking only for topmost space - doesn't always suffice to trigger trimming. To compensate for this, - free() will, with a period of MAX_RELEASE_CHECK_RATE (or the - current number of segments, if greater) try to release unused - segments to the OS when freeing chunks that result in - consolidation. The best value for this parameter is a compromise - between slowing down frees with relatively costly checks that - rarely trigger versus holding on to unused memory. To effectively - disable, set to MAX_SIZE_T. This may lead to a very slight speed - improvement at the expense of carrying around more memory. -*/ - -/* Version identifier to allow people to support multiple versions */ -#ifndef DLMALLOC_VERSION -#define DLMALLOC_VERSION 20806 -#endif /* DLMALLOC_VERSION */ - -#ifndef DLMALLOC_EXPORT -#define DLMALLOC_EXPORT extern -#endif - -#ifndef WIN32 -#ifdef _WIN32 -#define WIN32 1 -#endif /* _WIN32 */ -#ifdef _WIN32_WCE -#define LACKS_FCNTL_H -#define WIN32 1 -#endif /* _WIN32_WCE */ -#endif /* WIN32 */ -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <tchar.h> -#define HAVE_MMAP 1 -#define HAVE_MORECORE 0 -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H -#define LACKS_STRING_H -#define LACKS_STRINGS_H -#define LACKS_SYS_TYPES_H -#define LACKS_ERRNO_H -#define LACKS_SCHED_H -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION -#endif /* MALLOC_FAILURE_ACTION */ -#ifndef MMAP_CLEARS -#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ -#define MMAP_CLEARS 0 -#else -#define MMAP_CLEARS 1 -#endif /* _WIN32_WCE */ -#endif /*MMAP_CLEARS */ -#endif /* WIN32 */ - -#if defined(DARWIN) || defined(_DARWIN) -/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ -#ifndef HAVE_MORECORE -#define HAVE_MORECORE 0 -#define HAVE_MMAP 1 -/* OSX allocators provide 16 byte alignment */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)16U) -#endif -#endif /* HAVE_MORECORE */ -#endif /* DARWIN */ - -#ifndef LACKS_SYS_TYPES_H -#include <sys/types.h> /* For size_t */ -#endif /* LACKS_SYS_TYPES_H */ - -/* The maximum possible size_t value has all bits set */ -#define MAX_SIZE_T (~(size_t)0) - -#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ -#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ - (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) -#endif /* USE_LOCKS */ - -#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ -#if ((defined(__GNUC__) && \ - ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ - defined(__i386__) || defined(__x86_64__))) || \ - (defined(_MSC_VER) && _MSC_VER>=1310)) -#ifndef USE_SPIN_LOCKS -#define USE_SPIN_LOCKS 1 -#endif /* USE_SPIN_LOCKS */ -#elif USE_SPIN_LOCKS -#error "USE_SPIN_LOCKS defined without implementation" -#endif /* ... locks available... */ -#elif !defined(USE_SPIN_LOCKS) -#define USE_SPIN_LOCKS 0 -#endif /* USE_LOCKS */ - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 -#endif /* ONLY_MSPACES */ -#ifndef MSPACES -#if ONLY_MSPACES -#define MSPACES 1 -#else /* ONLY_MSPACES */ -#define MSPACES 0 -#endif /* ONLY_MSPACES */ -#endif /* MSPACES */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) -#endif /* MALLOC_ALIGNMENT */ -#ifndef FOOTERS -#define FOOTERS 0 -#endif /* FOOTERS */ -#ifndef ABORT -#define ABORT abort() -#endif /* ABORT */ -#ifndef ABORT_ON_ASSERT_FAILURE -#define ABORT_ON_ASSERT_FAILURE 1 -#endif /* ABORT_ON_ASSERT_FAILURE */ -#ifndef PROCEED_ON_ERROR -#define PROCEED_ON_ERROR 0 -#endif /* PROCEED_ON_ERROR */ - -#ifndef INSECURE -#define INSECURE 0 -#endif /* INSECURE */ -#ifndef MALLOC_INSPECT_ALL -#define MALLOC_INSPECT_ALL 0 -#endif /* MALLOC_INSPECT_ALL */ -#ifndef HAVE_MMAP -#define HAVE_MMAP 1 -#endif /* HAVE_MMAP */ -#ifndef MMAP_CLEARS -#define MMAP_CLEARS 1 -#endif /* MMAP_CLEARS */ -#ifndef HAVE_MREMAP -#ifdef linux -#define HAVE_MREMAP 1 -#define _GNU_SOURCE /* Turns on mremap() definition */ -#else /* linux */ -#define HAVE_MREMAP 0 -#endif /* linux */ -#endif /* HAVE_MREMAP */ -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION errno = ENOMEM; -#endif /* MALLOC_FAILURE_ACTION */ -#ifndef HAVE_MORECORE -#if ONLY_MSPACES -#define HAVE_MORECORE 0 -#else /* ONLY_MSPACES */ -#define HAVE_MORECORE 1 -#endif /* ONLY_MSPACES */ -#endif /* HAVE_MORECORE */ -#if !HAVE_MORECORE -#define MORECORE_CONTIGUOUS 0 -#else /* !HAVE_MORECORE */ -#define MORECORE_DEFAULT sbrk -#ifndef MORECORE_CONTIGUOUS -#define MORECORE_CONTIGUOUS 1 -#endif /* MORECORE_CONTIGUOUS */ -#endif /* HAVE_MORECORE */ -#ifndef DEFAULT_GRANULARITY -#if (MORECORE_CONTIGUOUS || defined(WIN32)) -#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ -#else /* MORECORE_CONTIGUOUS */ -#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) -#endif /* MORECORE_CONTIGUOUS */ -#endif /* DEFAULT_GRANULARITY */ -#ifndef DEFAULT_TRIM_THRESHOLD -#ifndef MORECORE_CANNOT_TRIM -#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) -#else /* MORECORE_CANNOT_TRIM */ -#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T -#endif /* MORECORE_CANNOT_TRIM */ -#endif /* DEFAULT_TRIM_THRESHOLD */ -#ifndef DEFAULT_MMAP_THRESHOLD -#if HAVE_MMAP -#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) -#else /* HAVE_MMAP */ -#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* DEFAULT_MMAP_THRESHOLD */ -#ifndef MAX_RELEASE_CHECK_RATE -#if HAVE_MMAP -#define MAX_RELEASE_CHECK_RATE 4095 -#else -#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* MAX_RELEASE_CHECK_RATE */ -#ifndef USE_BUILTIN_FFS -#define USE_BUILTIN_FFS 0 -#endif /* USE_BUILTIN_FFS */ -#ifndef USE_DEV_RANDOM -#define USE_DEV_RANDOM 0 -#endif /* USE_DEV_RANDOM */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef NO_MALLOC_STATS -#define NO_MALLOC_STATS 0 -#endif /* NO_MALLOC_STATS */ -#ifndef NO_SEGMENT_TRAVERSAL -#define NO_SEGMENT_TRAVERSAL 0 -#endif /* NO_SEGMENT_TRAVERSAL */ - -/* - mallopt tuning options. SVID/XPG defines four standard parameter - numbers for mallopt, normally defined in malloc.h. None of these - are used in this malloc, so setting them has no effect. But this - malloc does support the following options. -*/ - -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) - -/* ------------------------ Mallinfo declarations ------------------------ */ - -#if !NO_MALLINFO -/* - This version of malloc supports the standard SVID/XPG mallinfo - routine that returns a struct containing usage properties and - statistics. It should work on any system that has a - /usr/include/malloc.h defining struct mallinfo. The main - declaration needed is the mallinfo struct that is returned (by-copy) - by mallinfo(). The malloinfo struct contains a bunch of fields that - are not even meaningful in this version of malloc. These fields are - are instead filled by mallinfo() with other numbers that might be of - interest. - - HAVE_USR_INCLUDE_MALLOC_H should be set if you have a - /usr/include/malloc.h file that includes a declaration of struct - mallinfo. If so, it is included; else a compliant version is - declared below. These must be precisely the same for mallinfo() to - work. The original SVID version of this struct, defined on most - systems with mallinfo, declares all fields as ints. But some others - define as unsigned long. If your system defines the fields using a - type of different width than listed here, you MUST #include your - system version and #define HAVE_USR_INCLUDE_MALLOC_H. -*/ - -/* #define HAVE_USR_INCLUDE_MALLOC_H */ - -#ifdef HAVE_USR_INCLUDE_MALLOC_H -#include "/usr/include/malloc.h" -#else /* HAVE_USR_INCLUDE_MALLOC_H */ -#ifndef STRUCT_MALLINFO_DECLARED -/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ -#define _STRUCT_MALLINFO -#define STRUCT_MALLINFO_DECLARED 1 -struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ -}; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* NO_MALLINFO */ - -/* - Try to persuade compilers to inline. The most critical functions for - inlining are defined as macros, so these aren't used for them. -*/ - -#ifndef FORCEINLINE - #if defined(__GNUC__) -#define FORCEINLINE __inline __attribute__ ((always_inline)) - #elif defined(_MSC_VER) - #define FORCEINLINE __forceinline - #endif -#endif -#ifndef NOINLINE - #if defined(__GNUC__) - #define NOINLINE __attribute__ ((noinline)) - #elif defined(_MSC_VER) - #define NOINLINE __declspec(noinline) - #else - #define NOINLINE - #endif -#endif - -#ifdef __cplusplus -extern "C" { -#ifndef FORCEINLINE - #define FORCEINLINE inline -#endif -#endif /* __cplusplus */ -#ifndef FORCEINLINE - #define FORCEINLINE -#endif - -#if !ONLY_MSPACES - -/* ------------------- Declarations of public routines ------------------- */ - -#ifndef USE_DL_PREFIX -#define dlcalloc calloc -#define dlfree free -#define dlmalloc malloc -#define dlmemalign memalign -#define dlposix_memalign posix_memalign -#define dlrealloc realloc -#define dlrealloc_in_place realloc_in_place -#define dlvalloc valloc -#define dlpvalloc pvalloc -#define dlmallinfo mallinfo -#define dlmallopt mallopt -#define dlmalloc_trim malloc_trim -#define dlmalloc_stats malloc_stats -#define dlmalloc_usable_size malloc_usable_size -#define dlmalloc_footprint malloc_footprint -#define dlmalloc_max_footprint malloc_max_footprint -#define dlmalloc_footprint_limit malloc_footprint_limit -#define dlmalloc_set_footprint_limit malloc_set_footprint_limit -#define dlmalloc_inspect_all malloc_inspect_all -#define dlindependent_calloc independent_calloc -#define dlindependent_comalloc independent_comalloc -#define dlbulk_free bulk_free -#endif /* USE_DL_PREFIX */ - -/* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. -*/ -DLMALLOC_EXPORT void* dlmalloc(size_t); - -/* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cause the current program to abort. -*/ -DLMALLOC_EXPORT void dlfree(void*); - -/* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. -*/ -DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); - -/* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. -*/ -DLMALLOC_EXPORT void* dlrealloc(void*, size_t); - -/* - realloc_in_place(void* p, size_t n) - Resizes the space allocated for p to size n, only if this can be - done without moving p (i.e., only if there is adjacent space - available if n is greater than p's current allocated size, or n is - less than or equal to p's size). This may be used instead of plain - realloc if an alternative allocation strategy is needed upon failure - to expand space; for example, reallocation of a buffer that must be - memory-aligned or cleared. You can use realloc_in_place to trigger - these alternatives only when needed. - - Returns p if successful; otherwise null. -*/ -DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); - -/* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. -*/ -DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); - -/* - int posix_memalign(void** pp, size_t alignment, size_t n); - Allocates a chunk of n bytes, aligned in accord with the alignment - argument. Differs from memalign only in that it (1) assigns the - allocated memory to *pp rather than returning it, (2) fails and - returns EINVAL if the alignment is not a power of two (3) fails and - returns ENOMEM if memory cannot be allocated. -*/ -DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); - -/* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. -*/ -DLMALLOC_EXPORT void* dlvalloc(size_t); - -/* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. To workaround the fact that mallopt is specified to use int, - not size_t parameters, the value -1 is specially treated as the - maximum unsigned size_t value. - - SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt. See below for details. Briefly, supported - parameters are as follows (listed defaults are for "typical" - configurations). - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) -*/ -DLMALLOC_EXPORT int dlmallopt(int, int); - -/* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. -*/ -DLMALLOC_EXPORT size_t dlmalloc_footprint(void); - -/* - malloc_max_footprint(); - Returns the maximum number of bytes obtained from the system. This - value will be greater than current footprint if deallocated space - has been reclaimed by the system. The peak number of bytes allocated - by malloc, realloc etc., is less than this value. Unlike mallinfo, - this function returns only a precomputed result, so can be called - frequently to monitor memory consumption. Even if locks are - otherwise defined, this function does not use them, so results might - not be up to date. -*/ -DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); - -/* - malloc_footprint_limit(); - Returns the number of bytes that the heap is allowed to obtain from - the system, returning the last value returned by - malloc_set_footprint_limit, or the maximum size_t value if - never set. The returned value reflects a permission. There is no - guarantee that this number of bytes can actually be obtained from - the system. -*/ -DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); - -/* - malloc_set_footprint_limit(); - Sets the maximum number of bytes to obtain from the system, causing - failure returns from malloc and related functions upon attempts to - exceed this value. The argument value may be subject to page - rounding to an enforceable limit; this actual value is returned. - Using an argument of the maximum possible size_t effectively - disables checks. If the argument is less than or equal to the - current malloc_footprint, then all future allocations that require - additional system memory will fail. However, invocation cannot - retroactively deallocate existing used memory. -*/ -DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); - -#if MALLOC_INSPECT_ALL -/* - malloc_inspect_all(void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg); - Traverses the heap and calls the given handler for each managed - region, skipping all bytes that are (or may be) used for bookkeeping - purposes. Traversal does not include include chunks that have been - directly memory mapped. Each reported region begins at the start - address, and continues up to but not including the end address. The - first used_bytes of the region contain allocated data. If - used_bytes is zero, the region is unallocated. The handler is - invoked with the given callback argument. If locks are defined, they - are held during the entire traversal. It is a bad idea to invoke - other malloc functions from within the handler. - - For example, to count the number of in-use chunks with size greater - than 1000, you could write: - static int count = 0; - void count_chunks(void* start, void* end, size_t used, void* arg) { - if (used >= 1000) ++count; - } - then: - malloc_inspect_all(count_chunks, NULL); - - malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. -*/ -DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), - void* arg); - -#endif /* MALLOC_INSPECT_ALL */ - -#if !NO_MALLINFO -/* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. -*/ -DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); -#endif /* NO_MALLINFO */ - -/* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be freed when it is no longer needed. This can be - done all at once using bulk_free. - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } -*/ -DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); - -/* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be freed when it is no longer needed. This can be - done all at once using bulk_free. - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. -*/ -DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); - -/* - bulk_free(void* array[], size_t n_elements) - Frees and clears (sets to null) each non-null pointer in the given - array. This is likely to be faster than freeing them one-by-one. - If footers are used, pointers that have been allocated in different - mspaces are not freed or cleared, and the count of all such pointers - is returned. For large arrays of pointers with poor locality, it - may be worthwhile to sort this array before calling bulk_free. -*/ -DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); - -/* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ -DLMALLOC_EXPORT void* dlpvalloc(size_t); - -/* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. -*/ -DLMALLOC_EXPORT int dlmalloc_trim(size_t); - -/* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. -*/ -DLMALLOC_EXPORT void dlmalloc_stats(void); - -/* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); -*/ -size_t dlmalloc_usable_size(void*); - -#endif /* ONLY_MSPACES */ - -#if MSPACES - -/* - mspace is an opaque type representing an independent - region of space that supports mspace_malloc, etc. -*/ -typedef void* mspace; - -/* - create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). -*/ -DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); - -/* - destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. -*/ -DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); - -/* - create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. -*/ -DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); - -/* - mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. -*/ -DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); - - -/* - mspace_malloc behaves as malloc, but operates within - the given space. -*/ -DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); - -/* - mspace_free behaves as free, but operates within - the given space. - - If compiled with FOOTERS==1, mspace_free is not actually needed. - free may be called instead of mspace_free because freed chunks from - any space are handled by their originating spaces. -*/ -DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); - -/* - mspace_realloc behaves as realloc, but operates within - the given space. - - If compiled with FOOTERS==1, mspace_realloc is not actually - needed. realloc may be called instead of mspace_realloc because - realloced chunks from any space are handled by their originating - spaces. -*/ -DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); - -/* - mspace_calloc behaves as calloc, but operates within - the given space. -*/ -DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); - -/* - mspace_memalign behaves as memalign, but operates within - the given space. -*/ -DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); - -/* - mspace_independent_calloc behaves as independent_calloc, but - operates within the given space. -*/ -DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); - -/* - mspace_independent_comalloc behaves as independent_comalloc, but - operates within the given space. -*/ -DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); - -/* - mspace_footprint() returns the number of bytes obtained from the - system for this space. -*/ -DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); - -/* - mspace_max_footprint() returns the peak number of bytes obtained from the - system for this space. -*/ -DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); - - -#if !NO_MALLINFO -/* - mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. -*/ -DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - -/* - malloc_usable_size(void* p) behaves the same as malloc_usable_size; -*/ -DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); - -/* - mspace_malloc_stats behaves as malloc_stats, but reports - properties of the given space. -*/ -DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); - -/* - mspace_trim behaves as malloc_trim, but - operates within the given space. -*/ -DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); - -/* - An alias for mallopt. -*/ -DLMALLOC_EXPORT int mspace_mallopt(int, int); - -#endif /* MSPACES */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif /* __cplusplus */ - -/* - ======================================================================== - To make a fully customizable malloc.h header file, cut everything - above this line, put into file malloc.h, edit to suit, and #include it - on the next line, as well as in programs that use this malloc. - ======================================================================== -*/ - -/* #include "malloc.h" */ - -/*------------------------------ internal #includes ---------------------- */ - -#ifdef _MSC_VER -#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ -#endif /* _MSC_VER */ -#if !NO_MALLOC_STATS -#include <stdio.h> /* for printing in malloc_stats */ -#endif /* NO_MALLOC_STATS */ -#ifndef LACKS_ERRNO_H -#include <errno.h> /* for MALLOC_FAILURE_ACTION */ -#endif /* LACKS_ERRNO_H */ -#ifdef DEBUG -#if ABORT_ON_ASSERT_FAILURE -#undef assert -#define assert(x) if(!(x)) ABORT -#else /* ABORT_ON_ASSERT_FAILURE */ -#include <assert.h> -#endif /* ABORT_ON_ASSERT_FAILURE */ -#else /* DEBUG */ -#ifndef assert -#define assert(x) -#endif -#define DEBUG 0 -#endif /* DEBUG */ -#if !defined(WIN32) && !defined(LACKS_TIME_H) -#include <time.h> /* for magic initialization */ -#endif /* WIN32 */ -#ifndef LACKS_STDLIB_H -#include <stdlib.h> /* for abort() */ -#endif /* LACKS_STDLIB_H */ -#ifndef LACKS_STRING_H -#include <string.h> /* for memset etc */ -#endif /* LACKS_STRING_H */ -#if USE_BUILTIN_FFS -#ifndef LACKS_STRINGS_H -#include <strings.h> /* for ffs */ -#endif /* LACKS_STRINGS_H */ -#endif /* USE_BUILTIN_FFS */ -#if HAVE_MMAP -#ifndef LACKS_SYS_MMAN_H -/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ -#if (defined(linux) && !defined(__USE_GNU)) -#define __USE_GNU 1 -#include <sys/mman.h> /* for mmap */ -#undef __USE_GNU -#else -#include <sys/mman.h> /* for mmap */ -#endif /* linux */ -#endif /* LACKS_SYS_MMAN_H */ -#ifndef LACKS_FCNTL_H -#include <fcntl.h> -#endif /* LACKS_FCNTL_H */ -#endif /* HAVE_MMAP */ -#ifndef LACKS_UNISTD_H -#include <unistd.h> /* for sbrk, sysconf */ -#else /* LACKS_UNISTD_H */ -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) -extern void* sbrk(ptrdiff_t); -#endif /* FreeBSD etc */ -#endif /* LACKS_UNISTD_H */ - -/* Declarations for locking */ -#if USE_LOCKS -#ifndef WIN32 -#if defined (__SVR4) && defined (__sun) /* solaris */ -#include <thread.h> -#elif !defined(LACKS_SCHED_H) -#include <sched.h> -#endif /* solaris or LACKS_SCHED_H */ -#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS -#include <pthread.h> -#endif /* USE_RECURSIVE_LOCKS ... */ -#elif defined(_MSC_VER) -#ifndef _M_AMD64 -/* These are already defined on AMD64 builds */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ -LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); -LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* _M_AMD64 */ -#pragma intrinsic (_InterlockedCompareExchange) -#pragma intrinsic (_InterlockedExchange) -#define interlockedcompareexchange _InterlockedCompareExchange -#define interlockedexchange _InterlockedExchange -#elif defined(WIN32) && defined(__GNUC__) -#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) -#define interlockedexchange __sync_lock_test_and_set -#endif /* Win32 */ -#else /* USE_LOCKS */ -#endif /* USE_LOCKS */ - -#ifndef LOCK_AT_FORK -#define LOCK_AT_FORK 0 -#endif - -/* Declarations for bit scanning on win32 */ -#if defined(_MSC_VER) && _MSC_VER>=1300 -#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ -unsigned char _BitScanForward(unsigned long *index, unsigned long mask); -unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#define BitScanForward _BitScanForward -#define BitScanReverse _BitScanReverse -#pragma intrinsic(_BitScanForward) -#pragma intrinsic(_BitScanReverse) -#endif /* BitScanForward */ -#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ - -#ifndef WIN32 -#ifndef malloc_getpagesize -# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ -# ifndef _SC_PAGE_SIZE -# define _SC_PAGE_SIZE _SC_PAGESIZE -# endif -# endif -# ifdef _SC_PAGE_SIZE -# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) -# else -# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) - extern size_t getpagesize(); -# define malloc_getpagesize getpagesize() -# else -# ifdef WIN32 /* use supplied emulation of getpagesize */ -# define malloc_getpagesize getpagesize() -# else -# ifndef LACKS_SYS_PARAM_H -# include <sys/param.h> -# endif -# ifdef EXEC_PAGESIZE -# define malloc_getpagesize EXEC_PAGESIZE -# else -# ifdef NBPG -# ifndef CLSIZE -# define malloc_getpagesize NBPG -# else -# define malloc_getpagesize (NBPG * CLSIZE) -# endif -# else -# ifdef NBPC -# define malloc_getpagesize NBPC -# else -# ifdef PAGESIZE -# define malloc_getpagesize PAGESIZE -# else /* just guess */ -# define malloc_getpagesize ((size_t)4096U) -# endif -# endif -# endif -# endif -# endif -# endif -# endif -#endif -#endif - -/* ------------------- size_t and alignment properties -------------------- */ - -/* The byte and bit size of a size_t */ -#define SIZE_T_SIZE (sizeof(size_t)) -#define SIZE_T_BITSIZE (sizeof(size_t) << 3) - -/* Some constants coerced to size_t */ -/* Annoying but necessary to avoid errors on some platforms */ -#define SIZE_T_ZERO ((size_t)0) -#define SIZE_T_ONE ((size_t)1) -#define SIZE_T_TWO ((size_t)2) -#define SIZE_T_FOUR ((size_t)4) -#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) -#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) -#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) -#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) - -/* The bit mask value corresponding to MALLOC_ALIGNMENT */ -#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) - -/* True if address a has acceptable alignment */ -#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) - -/* the number of bytes to offset an address to align it */ -#define align_offset(A)\ - ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ - ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) - -/* -------------------------- MMAP preliminaries ------------------------- */ - -/* - If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and - checks to fail so compiler optimizer can delete code rather than - using so many "#if"s. -*/ - - -/* MORECORE and MMAP must return MFAIL on failure */ -#define MFAIL ((void*)(MAX_SIZE_T)) -#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ - -#if HAVE_MMAP - -#ifndef WIN32 -#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) -#define MMAP_PROT (PROT_READ|PROT_WRITE) -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif /* MAP_ANON */ -#ifdef MAP_ANONYMOUS -#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) -#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) -#else /* MAP_ANONYMOUS */ -/* - Nearly all versions of mmap support MAP_ANONYMOUS, so the following - is unlikely to be needed, but is supplied just in case. -*/ -#define MMAP_FLAGS (MAP_PRIVATE) -static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ -#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ - (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) -#endif /* MAP_ANONYMOUS */ - -#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) - -#else /* WIN32 */ - -/* Win32 MMAP via VirtualAlloc */ -static FORCEINLINE void* win32mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; -} - -/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ -static FORCEINLINE void* win32direct_mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); - return (ptr != 0)? ptr: MFAIL; -} - -/* This function supports releasing coalesed segments */ -static FORCEINLINE int win32munmap(void* ptr, size_t size) { - MEMORY_BASIC_INFORMATION minfo; - char* cptr = (char*)ptr; - while (size) { - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) - return -1; - if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || - minfo.State != MEM_COMMIT || minfo.RegionSize > size) - return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) - return -1; - cptr += minfo.RegionSize; - size -= minfo.RegionSize; - } - return 0; -} - -#define MMAP_DEFAULT(s) win32mmap(s) -#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) -#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) -#endif /* WIN32 */ -#endif /* HAVE_MMAP */ - -#if HAVE_MREMAP -#ifndef WIN32 -#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) -#endif /* WIN32 */ -#endif /* HAVE_MREMAP */ - -/** - * Define CALL_MORECORE - */ -#if HAVE_MORECORE - #ifdef MORECORE - #define CALL_MORECORE(S) MORECORE(S) - #else /* MORECORE */ - #define CALL_MORECORE(S) MORECORE_DEFAULT(S) - #endif /* MORECORE */ -#else /* HAVE_MORECORE */ - #define CALL_MORECORE(S) MFAIL -#endif /* HAVE_MORECORE */ - -/** - * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP - */ -#if HAVE_MMAP - #define USE_MMAP_BIT (SIZE_T_ONE) - - #ifdef MMAP - #define CALL_MMAP(s) MMAP(s) - #else /* MMAP */ - #define CALL_MMAP(s) MMAP_DEFAULT(s) - #endif /* MMAP */ - #ifdef MUNMAP - #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) - #else /* MUNMAP */ - #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) - #endif /* MUNMAP */ - #ifdef DIRECT_MMAP - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) - #else /* DIRECT_MMAP */ - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) - #endif /* DIRECT_MMAP */ -#else /* HAVE_MMAP */ - #define USE_MMAP_BIT (SIZE_T_ZERO) - - #define MMAP(s) MFAIL - #define MUNMAP(a, s) (-1) - #define DIRECT_MMAP(s) MFAIL - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) - #define CALL_MMAP(s) MMAP(s) - #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#endif /* HAVE_MMAP */ - -/** - * Define CALL_MREMAP - */ -#if HAVE_MMAP && HAVE_MREMAP - #ifdef MREMAP - #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) - #else /* MREMAP */ - #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) - #endif /* MREMAP */ -#else /* HAVE_MMAP && HAVE_MREMAP */ - #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL -#endif /* HAVE_MMAP && HAVE_MREMAP */ - -/* mstate bit set if continguous morecore disabled or failed */ -#define USE_NONCONTIGUOUS_BIT (4U) - -/* segment bit set in create_mspace_with_base */ -#define EXTERN_BIT (8U) - - -/* --------------------------- Lock preliminaries ------------------------ */ - -/* - When locks are defined, there is one global lock, plus - one per-mspace lock. - - The global lock_ensures that mparams.magic and other unique - mparams values are initialized only once. It also protects - sequences of calls to MORECORE. In many cases sys_alloc requires - two calls, that should not be interleaved with calls by other - threads. This does not protect against direct calls to MORECORE - by other threads not using this lock, so there is still code to - cope the best we can on interference. - - Per-mspace locks surround calls to malloc, free, etc. - By default, locks are simple non-reentrant mutexes. - - Because lock-protected regions generally have bounded times, it is - OK to use the supplied simple spinlocks. Spinlocks are likely to - improve performance for lightly contended applications, but worsen - performance under heavy contention. - - If USE_LOCKS is > 1, the definitions of lock routines here are - bypassed, in which case you will need to define the type MLOCK_T, - and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK - and TRY_LOCK. You must also declare a - static MLOCK_T malloc_global_mutex = { initialization values };. - -*/ - -#if !USE_LOCKS -#define USE_LOCK_BIT (0U) -#define INITIAL_LOCK(l) (0) -#define DESTROY_LOCK(l) (0) -#define ACQUIRE_MALLOC_GLOBAL_LOCK() -#define RELEASE_MALLOC_GLOBAL_LOCK() - -#else -#if USE_LOCKS > 1 -/* ----------------------- User-defined locks ------------------------ */ -/* Define your own lock implementation here */ -/* #define INITIAL_LOCK(lk) ... */ -/* #define DESTROY_LOCK(lk) ... */ -/* #define ACQUIRE_LOCK(lk) ... */ -/* #define RELEASE_LOCK(lk) ... */ -/* #define TRY_LOCK(lk) ... */ -/* static MLOCK_T malloc_global_mutex = ... */ - -#elif USE_SPIN_LOCKS - -/* First, define CAS_LOCK and CLEAR_LOCK on ints */ -/* Note CAS_LOCK defined to return 0 on success */ - -#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) -#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) -#define CLEAR_LOCK(sl) __sync_lock_release(sl) - -#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) -/* Custom spin locks for older gcc on x86 */ -static FORCEINLINE int x86_cas_lock(int *sl) { - int ret; - int val = 1; - int cmp = 0; - __asm__ __volatile__ ("lock; cmpxchgl %1, %2" - : "=a" (ret) - : "r" (val), "m" (*(sl)), "0"(cmp) - : "memory", "cc"); - return ret; -} - -static FORCEINLINE void x86_clear_lock(int* sl) { - assert(*sl != 0); - int prev = 0; - int ret; - __asm__ __volatile__ ("lock; xchgl %0, %1" - : "=r" (ret) - : "m" (*(sl)), "0"(prev) - : "memory"); -} - -#define CAS_LOCK(sl) x86_cas_lock(sl) -#define CLEAR_LOCK(sl) x86_clear_lock(sl) - -#else /* Win32 MSC */ -#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) -#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) - -#endif /* ... gcc spins locks ... */ - -/* How to yield for a spin lock */ -#define SPINS_PER_YIELD 63 -#if defined(_MSC_VER) -#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ -#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) -#elif defined (__SVR4) && defined (__sun) /* solaris */ -#define SPIN_LOCK_YIELD thr_yield(); -#elif !defined(LACKS_SCHED_H) -#define SPIN_LOCK_YIELD sched_yield(); -#else -#define SPIN_LOCK_YIELD -#endif /* ... yield ... */ - -#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 -/* Plain spin locks use single word (embedded in malloc_states) */ -static int spin_acquire_lock(int *sl) { - int spins = 0; - while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { - if ((++spins & SPINS_PER_YIELD) == 0) { - SPIN_LOCK_YIELD; - } - } - return 0; -} - -#define MLOCK_T int -#define TRY_LOCK(sl) !CAS_LOCK(sl) -#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) -#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) -#define INITIAL_LOCK(sl) (*sl = 0) -#define DESTROY_LOCK(sl) (0) -static MLOCK_T malloc_global_mutex = 0; - -#else /* USE_RECURSIVE_LOCKS */ -/* types for lock owners */ -#ifdef WIN32 -#define THREAD_ID_T DWORD -#define CURRENT_THREAD GetCurrentThreadId() -#define EQ_OWNER(X,Y) ((X) == (Y)) -#else -/* - Note: the following assume that pthread_t is a type that can be - initialized to (casted) zero. If this is not the case, you will need to - somehow redefine these or not use spin locks. -*/ -#define THREAD_ID_T pthread_t -#define CURRENT_THREAD pthread_self() -#define EQ_OWNER(X,Y) pthread_equal(X, Y) -#endif - -struct malloc_recursive_lock { - int sl; - unsigned int c; - THREAD_ID_T threadid; -}; - -#define MLOCK_T struct malloc_recursive_lock -static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; - -static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { - assert(lk->sl != 0); - if (--lk->c == 0) { - CLEAR_LOCK(&lk->sl); - } -} - -static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { - THREAD_ID_T mythreadid = CURRENT_THREAD; - int spins = 0; - for (;;) { - if (*((volatile int *)(&lk->sl)) == 0) { - if (!CAS_LOCK(&lk->sl)) { - lk->threadid = mythreadid; - lk->c = 1; - return 0; - } - } - else if (EQ_OWNER(lk->threadid, mythreadid)) { - ++lk->c; - return 0; - } - if ((++spins & SPINS_PER_YIELD) == 0) { - SPIN_LOCK_YIELD; - } - } -} - -static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { - THREAD_ID_T mythreadid = CURRENT_THREAD; - if (*((volatile int *)(&lk->sl)) == 0) { - if (!CAS_LOCK(&lk->sl)) { - lk->threadid = mythreadid; - lk->c = 1; - return 1; - } - } - else if (EQ_OWNER(lk->threadid, mythreadid)) { - ++lk->c; - return 1; - } - return 0; -} - -#define RELEASE_LOCK(lk) recursive_release_lock(lk) -#define TRY_LOCK(lk) recursive_try_lock(lk) -#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) -#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) -#define DESTROY_LOCK(lk) (0) -#endif /* USE_RECURSIVE_LOCKS */ - -#elif defined(WIN32) /* Win32 critical sections */ -#define MLOCK_T CRITICAL_SECTION -#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) -#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) -#define TRY_LOCK(lk) TryEnterCriticalSection(lk) -#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) -#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) -#define NEED_GLOBAL_LOCK_INIT - -static MLOCK_T malloc_global_mutex; -static volatile LONG malloc_global_mutex_status; - -/* Use spin loop to initialize global lock */ -static void init_malloc_global_mutex() { - for (;;) { - long stat = malloc_global_mutex_status; - if (stat > 0) - return; - /* transition to < 0 while initializing, then to > 0) */ - if (stat == 0 && - interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { - InitializeCriticalSection(&malloc_global_mutex); - interlockedexchange(&malloc_global_mutex_status, (LONG)1); - return; - } - SleepEx(0, FALSE); - } -} - -#else /* pthreads-based locks */ -#define MLOCK_T pthread_mutex_t -#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) -#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) -#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) -#define INITIAL_LOCK(lk) pthread_init_lock(lk) -#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) - -#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) -/* Cope with old-style linux recursive lock initialization by adding */ -/* skipped internal declaration from pthread.h */ -extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, - int __kind)); -#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP -#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) -#endif /* USE_RECURSIVE_LOCKS ... */ - -static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; - -static int pthread_init_lock (MLOCK_T *lk) { - pthread_mutexattr_t attr; - if (pthread_mutexattr_init(&attr)) return 1; -#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; -#endif - if (pthread_mutex_init(lk, &attr)) return 1; - if (pthread_mutexattr_destroy(&attr)) return 1; - return 0; -} - -#endif /* ... lock types ... */ - -/* Common code for all lock types */ -#define USE_LOCK_BIT (2U) - -#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK -#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); -#endif - -#ifndef RELEASE_MALLOC_GLOBAL_LOCK -#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); -#endif - -#endif /* USE_LOCKS */ - -/* ----------------------- Chunk representations ------------------------ */ - -/* - (The following includes lightly edited explanations by Colin Plumb.) - - The malloc_chunk declaration below is misleading (but accurate and - necessary). It declares a "view" into memory allowing access to - necessary fields at known offsets from a given base. - - Chunks of memory are maintained using a `boundary tag' method as - originally described by Knuth. (See the paper by Paul Wilson - ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such - techniques.) Sizes of free chunks are stored both in the front of - each chunk and at the end. This makes consolidating fragmented - chunks into bigger chunks fast. The head fields also hold bits - representing whether chunks are free or in use. - - Here are some pictures to make it clearer. They are "exploded" to - show that the state of a chunk can be thought of as extending from - the high 31 bits of the head field of its header through the - prev_foot and PINUSE_BIT bit of the following chunk header. - - A chunk that's in use looks like: - - chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk (if P = 0) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| - | Size of this chunk 1| +-+ - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - +- -+ - | | - +- -+ - | : - +- size - sizeof(size_t) available payload bytes -+ - : | - chunk-> +- -+ - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| - | Size of next chunk (may or may not be in use) | +-+ - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - And if it's free, it looks like this: - - chunk-> +- -+ - | User payload (must be in use, or we would have merged!) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| - | Size of this chunk 0| +-+ - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Next pointer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Prev pointer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | : - +- size - sizeof(struct chunk) unused bytes -+ - : | - chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of this chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| - | Size of next chunk (must be in use, or we would have merged)| +-+ - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | : - +- User payload -+ - : | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |0| - +-+ - Note that since we always merge adjacent free chunks, the chunks - adjacent to a free chunk must be in use. - - Given a pointer to a chunk (which can be derived trivially from the - payload pointer) we can, in O(1) time, find out whether the adjacent - chunks are free, and if so, unlink them from the lists that they - are on and merge them with the current chunk. - - Chunks always begin on even word boundaries, so the mem portion - (which is returned to the user) is also on an even word boundary, and - thus at least double-word aligned. - - The P (PINUSE_BIT) bit, stored in the unused low-order bit of the - chunk size (which is always a multiple of two words), is an in-use - bit for the *previous* chunk. If that bit is *clear*, then the - word before the current chunk size contains the previous chunk - size, and can be used to find the front of the previous chunk. - The very first chunk allocated always has this bit set, preventing - access to non-existent (or non-owned) memory. If pinuse is set for - any given chunk, then you CANNOT determine the size of the - previous chunk, and might even get a memory addressing fault when - trying to do so. - - The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of - the chunk size redundantly records whether the current chunk is - inuse (unless the chunk is mmapped). This redundancy enables usage - checks within free and realloc, and reduces indirection when freeing - and consolidating chunks. - - Each freshly allocated chunk must have both cinuse and pinuse set. - That is, each allocated chunk borders either a previously allocated - and still in-use chunk, or the base of its memory arena. This is - ensured by making all allocations from the `lowest' part of any - found chunk. Further, no free chunk physically borders another one, - so each free chunk is known to be preceded and followed by either - inuse chunks or the ends of memory. - - Note that the `foot' of the current chunk is actually represented - as the prev_foot of the NEXT chunk. This makes it easier to - deal with alignments etc but can be very confusing when trying - to extend or adapt this code. - - The exceptions to all this are - - 1. The special chunk `top' is the top-most available chunk (i.e., - the one bordering the end of available memory). It is treated - specially. Top is never included in any bin, is used only if - no other chunk is available, and is released back to the - system if it is very large (see M_TRIM_THRESHOLD). In effect, - the top chunk is treated as larger (and thus less well - fitting) than any other available chunk. The top chunk - doesn't update its trailing size field since there is no next - contiguous chunk that would have to index off it. However, - space is still allocated for it (TOP_FOOT_SIZE) to enable - separation or merging when space is extended. - - 3. Chunks allocated via mmap, have both cinuse and pinuse bits - cleared in their head fields. Because they are allocated - one-by-one, each must carry its own prev_foot field, which is - also used to hold the offset this chunk has within its mmapped - region, which is needed to preserve alignment. Each mmapped - chunk is trailed by the first two fields of a fake next-chunk - for sake of usage checks. - -*/ - -struct malloc_chunk { - size_t prev_foot; /* Size of previous chunk (if free). */ - size_t head; /* Size and inuse bits. */ - struct malloc_chunk* fd; /* double links -- used only if free. */ - struct malloc_chunk* bk; -}; - -typedef struct malloc_chunk mchunk; -typedef struct malloc_chunk* mchunkptr; -typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ -typedef unsigned int bindex_t; /* Described below */ -typedef unsigned int binmap_t; /* Described below */ -typedef unsigned int flag_t; /* The type of various bit flag sets */ - -/* ------------------- Chunks sizes and alignments ----------------------- */ - -#define MCHUNK_SIZE (sizeof(mchunk)) - -#if FOOTERS -#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -#else /* FOOTERS */ -#define CHUNK_OVERHEAD (SIZE_T_SIZE) -#endif /* FOOTERS */ - -/* MMapped chunks need a second word of overhead ... */ -#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -/* ... and additional padding for fake next-chunk at foot */ -#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) - -/* The smallest size we can malloc is an aligned minimal chunk */ -#define MIN_CHUNK_SIZE\ - ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* conversion from malloc headers to user pointers, and back */ -#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) -#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) -/* chunk associated with aligned address A */ -#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) - -/* Bounds on request (not chunk) sizes. */ -#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) -#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) - -/* pad request bytes into a usable size */ -#define pad_request(req) \ - (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* pad request, checking for minimum (but not maximum) */ -#define request2size(req) \ - (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) - - -/* ------------------ Operations on head and foot fields ----------------- */ - -/* - The head field of a chunk is or'ed with PINUSE_BIT when previous - adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in - use, unless mmapped, in which case both bits are cleared. - - FLAG4_BIT is not used by this malloc, but might be useful in extensions. -*/ - -#define PINUSE_BIT (SIZE_T_ONE) -#define CINUSE_BIT (SIZE_T_TWO) -#define FLAG4_BIT (SIZE_T_FOUR) -#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) -#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) - -/* Head value for fenceposts */ -#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) - -/* extraction of fields from head words */ -#define cinuse(p) ((p)->head & CINUSE_BIT) -#define pinuse(p) ((p)->head & PINUSE_BIT) -#define flag4inuse(p) ((p)->head & FLAG4_BIT) -#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) -#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) - -#define chunksize(p) ((p)->head & ~(FLAG_BITS)) - -#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) -#define set_flag4(p) ((p)->head |= FLAG4_BIT) -#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) - -/* Treat space at ptr +/- offset as a chunk */ -#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) -#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) - -/* Ptr to next or previous physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) -#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) - -/* extract next chunk's pinuse bit */ -#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) - -/* Get/set size at footer */ -#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) -#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) - -/* Set size, pinuse bit, and foot */ -#define set_size_and_pinuse_of_free_chunk(p, s)\ - ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) - -/* Set size, pinuse bit, foot, and clear next pinuse */ -#define set_free_with_pinuse(p, s, n)\ - (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) - -/* Get the internal overhead associated with chunk p */ -#define overhead_for(p)\ - (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) - -/* Return true if malloced space is not necessarily cleared */ -#if MMAP_CLEARS -#define calloc_must_clear(p) (!is_mmapped(p)) -#else /* MMAP_CLEARS */ -#define calloc_must_clear(p) (1) -#endif /* MMAP_CLEARS */ - -/* ---------------------- Overlaid data structures ----------------------- */ - -/* - When chunks are not in use, they are treated as nodes of either - lists or trees. - - "Small" chunks are stored in circular doubly-linked lists, and look - like this: - - chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - `head:' | Size of chunk, in bytes |P| - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Forward pointer to next chunk in list | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Back pointer to previous chunk in list | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Unused space (may be 0 bytes long) . - . . - . | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - `foot:' | Size of chunk, in bytes | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Larger chunks are kept in a form of bitwise digital trees (aka - tries) keyed on chunksizes. Because malloc_tree_chunks are only for - free chunks greater than 256 bytes, their size doesn't impose any - constraints on user chunk sizes. Each node looks like: - - chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - `head:' | Size of chunk, in bytes |P| - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Forward pointer to next chunk of same size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Back pointer to previous chunk of same size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer to left child (child[0]) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer to right child (child[1]) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer to parent | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | bin index of this chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Unused space . - . | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - `foot:' | Size of chunk, in bytes | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Each tree holding treenodes is a tree of unique chunk sizes. Chunks - of the same size are arranged in a circularly-linked list, with only - the oldest chunk (the next to be used, in our FIFO ordering) - actually in the tree. (Tree members are distinguished by a non-null - parent pointer.) If a chunk with the same size an an existing node - is inserted, it is linked off the existing node using pointers that - work in the same way as fd/bk pointers of small chunks. - - Each tree contains a power of 2 sized range of chunk sizes (the - smallest is 0x100 <= x < 0x180), which is is divided in half at each - tree level, with the chunks in the smaller half of the range (0x100 - <= x < 0x140 for the top nose) in the left subtree and the larger - half (0x140 <= x < 0x180) in the right subtree. This is, of course, - done by inspecting individual bits. - - Using these rules, each node's left subtree contains all smaller - sizes than its right subtree. However, the node at the root of each - subtree has no particular ordering relationship to either. (The - dividing line between the subtree sizes is based on trie relation.) - If we remove the last chunk of a given size from the interior of the - tree, we need to replace it with a leaf node. The tree ordering - rules permit a node to be replaced by any leaf below it. - - The smallest chunk in a tree (a common operation in a best-fit - allocator) can be found by walking a path to the leftmost leaf in - the tree. Unlike a usual binary tree, where we follow left child - pointers until we reach a null, here we follow the right child - pointer any time the left one is null, until we reach a leaf with - both child pointers null. The smallest chunk in the tree will be - somewhere along that path. - - The worst case number of steps to add, find, or remove a node is - bounded by the number of bits differentiating chunks within - bins. Under current bin calculations, this ranges from 6 up to 21 - (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case - is of course much better. -*/ - -struct malloc_tree_chunk { - /* The first four fields must be compatible with malloc_chunk */ - size_t prev_foot; - size_t head; - struct malloc_tree_chunk* fd; - struct malloc_tree_chunk* bk; - - struct malloc_tree_chunk* child[2]; - struct malloc_tree_chunk* parent; - bindex_t index; -}; - -typedef struct malloc_tree_chunk tchunk; -typedef struct malloc_tree_chunk* tchunkptr; -typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ - -/* A little helper macro for trees */ -#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) - -/* ----------------------------- Segments -------------------------------- */ - -/* - Each malloc space may include non-contiguous segments, held in a - list headed by an embedded malloc_segment record representing the - top-most space. Segments also include flags holding properties of - the space. Large chunks that are directly allocated by mmap are not - included in this list. They are instead independently created and - destroyed without otherwise keeping track of them. - - Segment management mainly comes into play for spaces allocated by - MMAP. Any call to MMAP might or might not return memory that is - adjacent to an existing segment. MORECORE normally contiguously - extends the current space, so this space is almost always adjacent, - which is simpler and faster to deal with. (This is why MORECORE is - used preferentially to MMAP when both are available -- see - sys_alloc.) When allocating using MMAP, we don't use any of the - hinting mechanisms (inconsistently) supported in various - implementations of unix mmap, or distinguish reserving from - committing memory. Instead, we just ask for space, and exploit - contiguity when we get it. It is probably possible to do - better than this on some systems, but no general scheme seems - to be significantly better. - - Management entails a simpler variant of the consolidation scheme - used for chunks to reduce fragmentation -- new adjacent memory is - normally prepended or appended to an existing segment. However, - there are limitations compared to chunk consolidation that mostly - reflect the fact that segment processing is relatively infrequent - (occurring only when getting memory from system) and that we - don't expect to have huge numbers of segments: - - * Segments are not indexed, so traversal requires linear scans. (It - would be possible to index these, but is not worth the extra - overhead and complexity for most programs on most platforms.) - * New segments are only appended to old ones when holding top-most - memory; if they cannot be prepended to others, they are held in - different segments. - - Except for the top-most segment of an mstate, each segment record - is kept at the tail of its segment. Segments are added by pushing - segment records onto the list headed by &mstate.seg for the - containing mstate. - - Segment flags control allocation/merge/deallocation policies: - * If EXTERN_BIT set, then we did not allocate this segment, - and so should not try to deallocate or merge with others. - (This currently holds only for the initial segment passed - into create_mspace_with_base.) - * If USE_MMAP_BIT set, the segment may be merged with - other surrounding mmapped segments and trimmed/de-allocated - using munmap. - * If neither bit is set, then the segment was obtained using - MORECORE so can be merged with surrounding MORECORE'd segments - and deallocated/trimmed using MORECORE with negative arguments. -*/ - -struct malloc_segment { - char* base; /* base address */ - size_t size; /* allocated size */ - struct malloc_segment* next; /* ptr to next segment */ - flag_t sflags; /* mmap and extern flag */ -}; - -#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) -#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) - -typedef struct malloc_segment msegment; -typedef struct malloc_segment* msegmentptr; - -/* ---------------------------- malloc_state ----------------------------- */ - -/* - A malloc_state holds all of the bookkeeping for a space. - The main fields are: - - Top - The topmost chunk of the currently active segment. Its size is - cached in topsize. The actual size of topmost space is - topsize+TOP_FOOT_SIZE, which includes space reserved for adding - fenceposts and segment records if necessary when getting more - space from the system. The size at which to autotrim top is - cached from mparams in trim_check, except that it is disabled if - an autotrim fails. - - Designated victim (dv) - This is the preferred chunk for servicing small requests that - don't have exact fits. It is normally the chunk split off most - recently to service another small request. Its size is cached in - dvsize. The link fields of this chunk are not maintained since it - is not kept in a bin. - - SmallBins - An array of bin headers for free chunks. These bins hold chunks - with sizes less than MIN_LARGE_SIZE bytes. Each bin contains - chunks of all the same size, spaced 8 bytes apart. To simplify - use in double-linked lists, each bin header acts as a malloc_chunk - pointing to the real first node, if it exists (else pointing to - itself). This avoids special-casing for headers. But to avoid - waste, we allocate only the fd/bk pointers of bins, and then use - repositioning tricks to treat these as the fields of a chunk. - - TreeBins - Treebins are pointers to the roots of trees holding a range of - sizes. There are 2 equally spaced treebins for each power of two - from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything - larger. - - Bin maps - There is one bit map for small bins ("smallmap") and one for - treebins ("treemap). Each bin sets its bit when non-empty, and - clears the bit when empty. Bit operations are then used to avoid - bin-by-bin searching -- nearly all "search" is done without ever - looking at bins that won't be selected. The bit maps - conservatively use 32 bits per map word, even if on 64bit system. - For a good description of some of the bit-based techniques used - here, see Henry S. Warren Jr's book "Hacker's Delight" (and - supplement at http://hackersdelight.org/). Many of these are - intended to reduce the branchiness of paths through malloc etc, as - well as to reduce the number of memory locations read or written. - - Segments - A list of segments headed by an embedded malloc_segment record - representing the initial space. - - Address check support - The least_addr field is the least address ever obtained from - MORECORE or MMAP. Attempted frees and reallocs of any address less - than this are trapped (unless INSECURE is defined). - - Magic tag - A cross-check field that should always hold same value as mparams.magic. - - Max allowed footprint - The maximum allowed bytes to allocate from system (zero means no limit) - - Flags - Bits recording whether to use MMAP, locks, or contiguous MORECORE - - Statistics - Each space keeps track of current and maximum system memory - obtained via MORECORE or MMAP. - - Trim support - Fields holding the amount of unused topmost memory that should trigger - trimming, and a counter to force periodic scanning to release unused - non-topmost segments. - - Locking - If USE_LOCKS is defined, the "mutex" lock is acquired and released - around every public call using this mspace. - - Extension support - A void* pointer and a size_t field that can be used to help implement - extensions to this malloc. -*/ - -/* Bin types, widths and sizes */ -#define NSMALLBINS (32U) -#define NTREEBINS (32U) -#define SMALLBIN_SHIFT (3U) -#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) -#define TREEBIN_SHIFT (8U) -#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) -#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) -#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) - -struct malloc_state { - binmap_t smallmap; - binmap_t treemap; - size_t dvsize; - size_t topsize; - char* least_addr; - mchunkptr dv; - mchunkptr top; - size_t trim_check; - size_t release_checks; - size_t magic; - mchunkptr smallbins[(NSMALLBINS+1)*2]; - tbinptr treebins[NTREEBINS]; - size_t footprint; - size_t max_footprint; - size_t footprint_limit; /* zero means no limit */ - flag_t mflags; -#if USE_LOCKS - MLOCK_T mutex; /* locate lock among fields that rarely change */ -#endif /* USE_LOCKS */ - msegment seg; - void* extp; /* Unused but available for extensions */ - size_t exts; -}; - -typedef struct malloc_state* mstate; - -/* ------------- Global malloc_state and malloc_params ------------------- */ - -/* - malloc_params holds global properties, including those that can be - dynamically set using mallopt. There is a single instance, mparams, - initialized in init_mparams. Note that the non-zeroness of "magic" - also serves as an initialization flag. -*/ - -struct malloc_params { - size_t magic; - size_t page_size; - size_t granularity; - size_t mmap_threshold; - size_t trim_threshold; - flag_t default_mflags; -}; - -static struct malloc_params mparams; - -/* Ensure mparams initialized */ -#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) - -#if !ONLY_MSPACES - -/* The global malloc_state used for all non-"mspace" calls */ -static struct malloc_state _gm_; -#define gm (&_gm_) -#define is_global(M) ((M) == &_gm_) - -#endif /* !ONLY_MSPACES */ - -#define is_initialized(M) ((M)->top != 0) - -/* -------------------------- system alloc setup ------------------------- */ - -/* Operations on mflags */ - -#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) -#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) -#if USE_LOCKS -#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) -#else -#define disable_lock(M) -#endif - -#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) -#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) -#if HAVE_MMAP -#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) -#else -#define disable_mmap(M) -#endif - -#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) -#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) - -#define set_lock(M,L)\ - ((M)->mflags = (L)?\ - ((M)->mflags | USE_LOCK_BIT) :\ - ((M)->mflags & ~USE_LOCK_BIT)) - -/* page-align a size */ -#define page_align(S)\ - (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) - -/* granularity-align a size */ -#define granularity_align(S)\ - (((S) + (mparams.granularity - SIZE_T_ONE))\ - & ~(mparams.granularity - SIZE_T_ONE)) - - -/* For mmap, use granularity alignment on windows, else page-align */ -#ifdef WIN32 -#define mmap_align(S) granularity_align(S) -#else -#define mmap_align(S) page_align(S) -#endif - -/* For sys_alloc, enough padding to ensure can malloc request on success */ -#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) - -#define is_page_aligned(S)\ - (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) -#define is_granularity_aligned(S)\ - (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) - -/* True if segment S holds address A */ -#define segment_holds(S, A)\ - ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) - -/* Return segment holding given address */ -static msegmentptr segment_holding(mstate m, char* addr) { - msegmentptr sp = &m->seg; - for (;;) { - if (addr >= sp->base && addr < sp->base + sp->size) - return sp; - if ((sp = sp->next) == 0) - return 0; - } -} - -/* Return true if segment contains a segment link */ -static int has_segment_link(mstate m, msegmentptr ss) { - msegmentptr sp = &m->seg; - for (;;) { - if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) - return 1; - if ((sp = sp->next) == 0) - return 0; - } -} - -#ifndef MORECORE_CANNOT_TRIM -#define should_trim(M,s) ((s) > (M)->trim_check) -#else /* MORECORE_CANNOT_TRIM */ -#define should_trim(M,s) (0) -#endif /* MORECORE_CANNOT_TRIM */ - -/* - TOP_FOOT_SIZE is padding at the end of a segment, including space - that may be needed to place segment records and fenceposts when new - noncontiguous segments are added. -*/ -#define TOP_FOOT_SIZE\ - (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) - - -/* ------------------------------- Hooks -------------------------------- */ - -/* - PREACTION should be defined to return 0 on success, and nonzero on - failure. If you are not using locking, you can redefine these to do - anything you like. -*/ - -#if USE_LOCKS -#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) -#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } -#else /* USE_LOCKS */ - -#ifndef PREACTION -#define PREACTION(M) (0) -#endif /* PREACTION */ - -#ifndef POSTACTION -#define POSTACTION(M) -#endif /* POSTACTION */ - -#endif /* USE_LOCKS */ - -/* - CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. - USAGE_ERROR_ACTION is triggered on detected bad frees and - reallocs. The argument p is an address that might have triggered the - fault. It is ignored by the two predefined actions, but might be - useful in custom actions that try to help diagnose errors. -*/ - -#if PROCEED_ON_ERROR - -/* A count of the number of corruption errors causing resets */ -int malloc_corruption_error_count; - -/* default corruption action */ -static void reset_on_error(mstate m); - -#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) -#define USAGE_ERROR_ACTION(m, p) - -#else /* PROCEED_ON_ERROR */ - -#ifndef CORRUPTION_ERROR_ACTION -#define CORRUPTION_ERROR_ACTION(m) ABORT -#endif /* CORRUPTION_ERROR_ACTION */ - -#ifndef USAGE_ERROR_ACTION -#define USAGE_ERROR_ACTION(m,p) ABORT -#endif /* USAGE_ERROR_ACTION */ - -#endif /* PROCEED_ON_ERROR */ - - -/* -------------------------- Debugging setup ---------------------------- */ - -#if ! DEBUG - -#define check_free_chunk(M,P) -#define check_inuse_chunk(M,P) -#define check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) -#define check_malloc_state(M) -#define check_top_chunk(M,P) - -#else /* DEBUG */ -#define check_free_chunk(M,P) do_check_free_chunk(M,P) -#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) -#define check_top_chunk(M,P) do_check_top_chunk(M,P) -#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) -#define check_malloc_state(M) do_check_malloc_state(M) - -static void do_check_any_chunk(mstate m, mchunkptr p); -static void do_check_top_chunk(mstate m, mchunkptr p); -static void do_check_mmapped_chunk(mstate m, mchunkptr p); -static void do_check_inuse_chunk(mstate m, mchunkptr p); -static void do_check_free_chunk(mstate m, mchunkptr p); -static void do_check_malloced_chunk(mstate m, void* mem, size_t s); -static void do_check_tree(mstate m, tchunkptr t); -static void do_check_treebin(mstate m, bindex_t i); -static void do_check_smallbin(mstate m, bindex_t i); -static void do_check_malloc_state(mstate m); -static int bin_find(mstate m, mchunkptr x); -static size_t traverse_and_check(mstate m); -#endif /* DEBUG */ - -/* ---------------------------- Indexing Bins ---------------------------- */ - -#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) -#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) -#define small_index2size(i) ((i) << SMALLBIN_SHIFT) -#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) - -/* addressing by index. See above about smallbin repositioning */ -#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) -#define treebin_at(M,i) (&((M)->treebins[i])) - -/* assign tree index for size S to variable I. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_tree_index(S, I)\ -{\ - unsigned int X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ - }\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K = _bit_scan_reverse (X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ - }\ -} - -#elif defined(_MSC_VER) && _MSC_VER>=1300 -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K;\ - _BitScanReverse((DWORD *) &K, (DWORD) X);\ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ - }\ -} - -#else /* GNUC */ -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int Y = (unsigned int)X;\ - unsigned int N = ((Y - 0x100) >> 16) & 8;\ - unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ - N += K;\ - N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ - K = 14 - N + ((Y <<= K) >> 15);\ - I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ - }\ -} -#endif /* GNUC */ - -/* Bit representing maximum resolved size in a treebin at i */ -#define bit_for_tree_index(i) \ - (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) - -/* Shift placing maximum resolved bit in a treebin at i as sign bit */ -#define leftshift_for_tree_index(i) \ - ((i == NTREEBINS-1)? 0 : \ - ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) - -/* The size of the smallest chunk held in bin with index i */ -#define minsize_for_tree_index(i) \ - ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ - (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) - - -/* ------------------------ Operations on bin maps ----------------------- */ - -/* bit corresponding to given index */ -#define idx2bit(i) ((binmap_t)(1) << (i)) - -/* Mark/Clear bits with given index */ -#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) -#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) -#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) - -#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) -#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) -#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) - -/* isolate the least set bit of a bitmap */ -#define least_bit(x) ((x) & -(x)) - -/* mask with all bits to left of least bit of x on */ -#define left_bits(x) ((x<<1) | -(x<<1)) - -/* mask with all bits to left of or equal to least bit of x on */ -#define same_or_left_bits(x) ((x) | -(x)) - -/* index corresponding to given bit. Use x86 asm if possible */ - -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - J = __builtin_ctz(X); \ - I = (bindex_t)J;\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - J = _bit_scan_forward (X); \ - I = (bindex_t)J;\ -} - -#elif defined(_MSC_VER) && _MSC_VER>=1300 -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - _BitScanForward((DWORD *) &J, X);\ - I = (bindex_t)J;\ -} - -#elif USE_BUILTIN_FFS -#define compute_bit2idx(X, I) I = ffs(X)-1 - -#else -#define compute_bit2idx(X, I)\ -{\ - unsigned int Y = X - 1;\ - unsigned int K = Y >> (16-4) & 16;\ - unsigned int N = K; Y >>= K;\ - N += K = Y >> (8-3) & 8; Y >>= K;\ - N += K = Y >> (4-2) & 4; Y >>= K;\ - N += K = Y >> (2-1) & 2; Y >>= K;\ - N += K = Y >> (1-0) & 1; Y >>= K;\ - I = (bindex_t)(N + Y);\ -} -#endif /* GNUC */ - - -/* ----------------------- Runtime Check Support ------------------------- */ - -/* - For security, the main invariant is that malloc/free/etc never - writes to a static address other than malloc_state, unless static - malloc_state itself has been corrupted, which cannot occur via - malloc (because of these checks). In essence this means that we - believe all pointers, sizes, maps etc held in malloc_state, but - check all of those linked or offsetted from other embedded data - structures. These checks are interspersed with main code in a way - that tends to minimize their run-time cost. - - When FOOTERS is defined, in addition to range checking, we also - verify footer fields of inuse chunks, which can be used guarantee - that the mstate controlling malloc/free is intact. This is a - streamlined version of the approach described by William Robertson - et al in "Run-time Detection of Heap-based Overflows" LISA'03 - http://www.usenix.org/events/lisa03/tech/robertson.html The footer - of an inuse chunk holds the xor of its mstate and a random seed, - that is checked upon calls to free() and realloc(). This is - (probabalistically) unguessable from outside the program, but can be - computed by any code successfully malloc'ing any chunk, so does not - itself provide protection against code that has already broken - security through some other means. Unlike Robertson et al, we - always dynamically check addresses of all offset chunks (previous, - next, etc). This turns out to be cheaper than relying on hashes. -*/ - -#if !INSECURE -/* Check if address a is at least as high as any from MORECORE or MMAP */ -#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) -/* Check if address of next chunk n is higher than base chunk p */ -#define ok_next(p, n) ((char*)(p) < (char*)(n)) -/* Check if p has inuse status */ -#define ok_inuse(p) is_inuse(p) -/* Check if p has its pinuse bit on */ -#define ok_pinuse(p) pinuse(p) - -#else /* !INSECURE */ -#define ok_address(M, a) (1) -#define ok_next(b, n) (1) -#define ok_inuse(p) (1) -#define ok_pinuse(p) (1) -#endif /* !INSECURE */ - -#if (FOOTERS && !INSECURE) -/* Check if (alleged) mstate m has expected magic field */ -#define ok_magic(M) ((M)->magic == mparams.magic) -#else /* (FOOTERS && !INSECURE) */ -#define ok_magic(M) (1) -#endif /* (FOOTERS && !INSECURE) */ - -/* In gcc, use __builtin_expect to minimize impact of checks */ -#if !INSECURE -#if defined(__GNUC__) && __GNUC__ >= 3 -#define RTCHECK(e) __builtin_expect(e, 1) -#else /* GNUC */ -#define RTCHECK(e) (e) -#endif /* GNUC */ -#else /* !INSECURE */ -#define RTCHECK(e) (1) -#endif /* !INSECURE */ - -/* macros to set up inuse chunks with or without footers */ - -#if !FOOTERS - -#define mark_inuse_foot(M,p,s) - -/* Macros for setting head/foot of non-mmapped chunks */ - -/* Set cinuse bit and pinuse bit of next chunk */ -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - -/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - -/* Set size, cinuse and pinuse bit of this chunk */ -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) - -#else /* FOOTERS */ - -/* Set foot of inuse chunk to be xor of mstate and seed */ -#define mark_inuse_foot(M,p,s)\ - (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) - -#define get_mstate_for(p)\ - ((mstate)(((mchunkptr)((char*)(p) +\ - (chunksize(p))))->prev_foot ^ mparams.magic)) - -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ - mark_inuse_foot(M,p,s)) - -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ - mark_inuse_foot(M,p,s)) - -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - mark_inuse_foot(M, p, s)) - -#endif /* !FOOTERS */ - -/* ---------------------------- setting mparams -------------------------- */ - -#if LOCK_AT_FORK -static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } -static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } -static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } -#endif /* LOCK_AT_FORK */ - -/* Initialize mparams */ -static int init_mparams(void) { -#ifdef NEED_GLOBAL_LOCK_INIT - if (malloc_global_mutex_status <= 0) - init_malloc_global_mutex(); -#endif - - ACQUIRE_MALLOC_GLOBAL_LOCK(); - if (mparams.magic == 0) { - size_t magic; - size_t psize; - size_t gsize; - -#ifndef WIN32 - psize = malloc_getpagesize; - gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); -#else /* WIN32 */ - { - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - psize = system_info.dwPageSize; - gsize = ((DEFAULT_GRANULARITY != 0)? - DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); - } -#endif /* WIN32 */ - - /* Sanity-check configuration: - size_t must be unsigned and as wide as pointer type. - ints must be at least 4 bytes. - alignment must be at least 8. - Alignment, min chunk size, and page size must all be powers of 2. - */ - if ((sizeof(size_t) != sizeof(char*)) || - (MAX_SIZE_T < MIN_CHUNK_SIZE) || - (sizeof(int) < 4) || - (MALLOC_ALIGNMENT < (size_t)8U) || - ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || - ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || - ((gsize & (gsize-SIZE_T_ONE)) != 0) || - ((psize & (psize-SIZE_T_ONE)) != 0)) - ABORT; - mparams.granularity = gsize; - mparams.page_size = psize; - mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; - mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; -#if MORECORE_CONTIGUOUS - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; -#else /* MORECORE_CONTIGUOUS */ - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; -#endif /* MORECORE_CONTIGUOUS */ - -#if !ONLY_MSPACES - /* Set up lock for main malloc area */ - gm->mflags = mparams.default_mflags; - (void)INITIAL_LOCK(&gm->mutex); -#endif -#if LOCK_AT_FORK - pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child); -#endif - - { -#if USE_DEV_RANDOM - int fd; - unsigned char buf[sizeof(size_t)]; - /* Try to use /dev/urandom, else fall back on using time */ - if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && - read(fd, buf, sizeof(buf)) == sizeof(buf)) { - magic = *((size_t *) buf); - close(fd); - } - else -#endif /* USE_DEV_RANDOM */ -#ifdef WIN32 - magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); -#elif defined(LACKS_TIME_H) - magic = (size_t)&magic ^ (size_t)0x55555555U; -#else - magic = (size_t)(time(0) ^ (size_t)0x55555555U); -#endif - magic |= (size_t)8U; /* ensure nonzero */ - magic &= ~(size_t)7U; /* improve chances of fault for bad values */ - /* Until memory modes commonly available, use volatile-write */ - (*(volatile size_t *)(&(mparams.magic))) = magic; - } - } - - RELEASE_MALLOC_GLOBAL_LOCK(); - return 1; -} - -/* support for mallopt */ -static int change_mparam(int param_number, int value) { - size_t val; - ensure_initialization(); - val = (value == -1)? MAX_SIZE_T : (size_t)value; - switch(param_number) { - case M_TRIM_THRESHOLD: - mparams.trim_threshold = val; - return 1; - case M_GRANULARITY: - if (val >= mparams.page_size && ((val & (val-1)) == 0)) { - mparams.granularity = val; - return 1; - } - else - return 0; - case M_MMAP_THRESHOLD: - mparams.mmap_threshold = val; - return 1; - default: - return 0; - } -} - -#if DEBUG -/* ------------------------- Debugging Support --------------------------- */ - -/* Check properties of any chunk, whether free, inuse, mmapped etc */ -static void do_check_any_chunk(mstate m, mchunkptr p) { - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); -} - -/* Check properties of top chunk */ -static void do_check_top_chunk(mstate m, mchunkptr p) { - msegmentptr sp = segment_holding(m, (char*)p); - size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ - assert(sp != 0); - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); - assert(sz == m->topsize); - assert(sz > 0); - assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); - assert(pinuse(p)); - assert(!pinuse(chunk_plus_offset(p, sz))); -} - -/* Check properties of (inuse) mmapped chunks */ -static void do_check_mmapped_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); - size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); - assert(is_mmapped(p)); - assert(use_mmap(m)); - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); - assert(ok_address(m, p)); - assert(!is_small(sz)); - assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); - assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); - assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); -} - -/* Check properties of inuse chunks */ -static void do_check_inuse_chunk(mstate m, mchunkptr p) { - do_check_any_chunk(m, p); - assert(is_inuse(p)); - assert(next_pinuse(p)); - /* If not pinuse and not mmapped, previous chunk has OK offset */ - assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); - if (is_mmapped(p)) - do_check_mmapped_chunk(m, p); -} - -/* Check properties of free chunks */ -static void do_check_free_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); - mchunkptr next = chunk_plus_offset(p, sz); - do_check_any_chunk(m, p); - assert(!is_inuse(p)); - assert(!next_pinuse(p)); - assert (!is_mmapped(p)); - if (p != m->dv && p != m->top) { - if (sz >= MIN_CHUNK_SIZE) { - assert((sz & CHUNK_ALIGN_MASK) == 0); - assert(is_aligned(chunk2mem(p))); - assert(next->prev_foot == sz); - assert(pinuse(p)); - assert (next == m->top || is_inuse(next)); - assert(p->fd->bk == p); - assert(p->bk->fd == p); - } - else /* markers are always of size SIZE_T_SIZE */ - assert(sz == SIZE_T_SIZE); - } -} - -/* Check properties of malloced chunks at the point they are malloced */ -static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - size_t sz = p->head & ~INUSE_BITS; - do_check_inuse_chunk(m, p); - assert((sz & CHUNK_ALIGN_MASK) == 0); - assert(sz >= MIN_CHUNK_SIZE); - assert(sz >= s); - /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ - assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); - } -} - -/* Check a tree and its subtrees. */ -static void do_check_tree(mstate m, tchunkptr t) { - tchunkptr head = 0; - tchunkptr u = t; - bindex_t tindex = t->index; - size_t tsize = chunksize(t); - bindex_t idx; - compute_tree_index(tsize, idx); - assert(tindex == idx); - assert(tsize >= MIN_LARGE_SIZE); - assert(tsize >= minsize_for_tree_index(idx)); - assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); - - do { /* traverse through chain of same-sized nodes */ - do_check_any_chunk(m, ((mchunkptr)u)); - assert(u->index == tindex); - assert(chunksize(u) == tsize); - assert(!is_inuse(u)); - assert(!next_pinuse(u)); - assert(u->fd->bk == u); - assert(u->bk->fd == u); - if (u->parent == 0) { - assert(u->child[0] == 0); - assert(u->child[1] == 0); - } - else { - assert(head == 0); /* only one node on chain has parent */ - head = u; - assert(u->parent != u); - assert (u->parent->child[0] == u || - u->parent->child[1] == u || - *((tbinptr*)(u->parent)) == u); - if (u->child[0] != 0) { - assert(u->child[0]->parent == u); - assert(u->child[0] != u); - do_check_tree(m, u->child[0]); - } - if (u->child[1] != 0) { - assert(u->child[1]->parent == u); - assert(u->child[1] != u); - do_check_tree(m, u->child[1]); - } - if (u->child[0] != 0 && u->child[1] != 0) { - assert(chunksize(u->child[0]) < chunksize(u->child[1])); - } - } - u = u->fd; - } while (u != t); - assert(head != 0); -} - -/* Check all the chunks in a treebin. */ -static void do_check_treebin(mstate m, bindex_t i) { - tbinptr* tb = treebin_at(m, i); - tchunkptr t = *tb; - int empty = (m->treemap & (1U << i)) == 0; - if (t == 0) - assert(empty); - if (!empty) - do_check_tree(m, t); -} - -/* Check all the chunks in a smallbin. */ -static void do_check_smallbin(mstate m, bindex_t i) { - sbinptr b = smallbin_at(m, i); - mchunkptr p = b->bk; - unsigned int empty = (m->smallmap & (1U << i)) == 0; - if (p == b) - assert(empty); - if (!empty) { - for (; p != b; p = p->bk) { - size_t size = chunksize(p); - mchunkptr q; - /* each chunk claims to be free */ - do_check_free_chunk(m, p); - /* chunk belongs in bin */ - assert(small_index(size) == i); - assert(p->bk == b || chunksize(p->bk) == chunksize(p)); - /* chunk is followed by an inuse chunk */ - q = next_chunk(p); - if (q->head != FENCEPOST_HEAD) - do_check_inuse_chunk(m, q); - } - } -} - -/* Find x in a bin. Used in other check functions. */ -static int bin_find(mstate m, mchunkptr x) { - size_t size = chunksize(x); - if (is_small(size)) { - bindex_t sidx = small_index(size); - sbinptr b = smallbin_at(m, sidx); - if (smallmap_is_marked(m, sidx)) { - mchunkptr p = b; - do { - if (p == x) - return 1; - } while ((p = p->fd) != b); - } - } - else { - bindex_t tidx; - compute_tree_index(size, tidx); - if (treemap_is_marked(m, tidx)) { - tchunkptr t = *treebin_at(m, tidx); - size_t sizebits = size << leftshift_for_tree_index(tidx); - while (t != 0 && chunksize(t) != size) { - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - sizebits <<= 1; - } - if (t != 0) { - tchunkptr u = t; - do { - if (u == (tchunkptr)x) - return 1; - } while ((u = u->fd) != t); - } - } - } - return 0; -} - -/* Traverse each chunk and check it; return total */ -static size_t traverse_and_check(mstate m) { - size_t sum = 0; - if (is_initialized(m)) { - msegmentptr s = &m->seg; - sum += m->topsize + TOP_FOOT_SIZE; - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - mchunkptr lastq = 0; - assert(pinuse(q)); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - sum += chunksize(q); - if (is_inuse(q)) { - assert(!bin_find(m, q)); - do_check_inuse_chunk(m, q); - } - else { - assert(q == m->dv || bin_find(m, q)); - assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ - do_check_free_chunk(m, q); - } - lastq = q; - q = next_chunk(q); - } - s = s->next; - } - } - return sum; -} - - -/* Check all properties of malloc_state. */ -static void do_check_malloc_state(mstate m) { - bindex_t i; - size_t total; - /* check bins */ - for (i = 0; i < NSMALLBINS; ++i) - do_check_smallbin(m, i); - for (i = 0; i < NTREEBINS; ++i) - do_check_treebin(m, i); - - if (m->dvsize != 0) { /* check dv chunk */ - do_check_any_chunk(m, m->dv); - assert(m->dvsize == chunksize(m->dv)); - assert(m->dvsize >= MIN_CHUNK_SIZE); - assert(bin_find(m, m->dv) == 0); - } - - if (m->top != 0) { /* check top chunk */ - do_check_top_chunk(m, m->top); - /*assert(m->topsize == chunksize(m->top)); redundant */ - assert(m->topsize > 0); - assert(bin_find(m, m->top) == 0); - } - - total = traverse_and_check(m); - assert(total <= m->footprint); - assert(m->footprint <= m->max_footprint); -} -#endif /* DEBUG */ - -/* ----------------------------- statistics ------------------------------ */ - -#if !NO_MALLINFO -static struct mallinfo internal_mallinfo(mstate m) { - struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - ensure_initialization(); - if (!PREACTION(m)) { - check_malloc_state(m); - if (is_initialized(m)) { - size_t nfree = SIZE_T_ONE; /* top always free */ - size_t mfree = m->topsize + TOP_FOOT_SIZE; - size_t sum = mfree; - msegmentptr s = &m->seg; - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - size_t sz = chunksize(q); - sum += sz; - if (!is_inuse(q)) { - mfree += sz; - ++nfree; - } - q = next_chunk(q); - } - s = s->next; - } - - nm.arena = sum; - nm.ordblks = nfree; - nm.hblkhd = m->footprint - sum; - nm.usmblks = m->max_footprint; - nm.uordblks = m->footprint - mfree; - nm.fordblks = mfree; - nm.keepcost = m->topsize; - } - - POSTACTION(m); - } - return nm; -} -#endif /* !NO_MALLINFO */ - -#if !NO_MALLOC_STATS -static void internal_malloc_stats(mstate m) { - ensure_initialization(); - if (!PREACTION(m)) { - size_t maxfp = 0; - size_t fp = 0; - size_t used = 0; - check_malloc_state(m); - if (is_initialized(m)) { - msegmentptr s = &m->seg; - maxfp = m->max_footprint; - fp = m->footprint; - used = fp - (m->topsize + TOP_FOOT_SIZE); - - while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - if (!is_inuse(q)) - used -= chunksize(q); - q = next_chunk(q); - } - s = s->next; - } - } - POSTACTION(m); /* drop lock */ - fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); - fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); - fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); - } -} -#endif /* NO_MALLOC_STATS */ - -/* ----------------------- Operations on smallbins ----------------------- */ - -/* - Various forms of linking and unlinking are defined as macros. Even - the ones for trees, which are very long but have very short typical - paths. This is ugly but reduces reliance on inlining support of - compilers. -*/ - -/* Link a free chunk into a smallbin */ -#define insert_small_chunk(M, P, S) {\ - bindex_t I = small_index(S);\ - mchunkptr B = smallbin_at(M, I);\ - mchunkptr F = B;\ - assert(S >= MIN_CHUNK_SIZE);\ - if (!smallmap_is_marked(M, I))\ - mark_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, B->fd)))\ - F = B->fd;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - B->fd = P;\ - F->bk = P;\ - P->fd = F;\ - P->bk = B;\ -} - -/* Unlink a chunk from a smallbin */ -#define unlink_small_chunk(M, P, S) {\ - mchunkptr F = P->fd;\ - mchunkptr B = P->bk;\ - bindex_t I = small_index(S);\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ - if (B == F) {\ - clear_smallmap(M, I);\ - }\ - else if (RTCHECK(B == smallbin_at(M,I) ||\ - (ok_address(M, B) && B->fd == P))) {\ - F->bk = B;\ - B->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ -} - -/* Unlink the first chunk from a smallbin */ -#define unlink_first_small_chunk(M, B, P, I) {\ - mchunkptr F = P->fd;\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (B == F) {\ - clear_smallmap(M, I);\ - }\ - else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ - F->bk = B;\ - B->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ -} - -/* Replace dv node, binning the old one */ -/* Used only when dvsize known to be small */ -#define replace_dv(M, P, S) {\ - size_t DVS = M->dvsize;\ - assert(is_small(DVS));\ - if (DVS != 0) {\ - mchunkptr DV = M->dv;\ - insert_small_chunk(M, DV, DVS);\ - }\ - M->dvsize = S;\ - M->dv = P;\ -} - -/* ------------------------- Operations on trees ------------------------- */ - -/* Insert chunk into tree */ -#define insert_large_chunk(M, X, S) {\ - tbinptr* H;\ - bindex_t I;\ - compute_tree_index(S, I);\ - H = treebin_at(M, I);\ - X->index = I;\ - X->child[0] = X->child[1] = 0;\ - if (!treemap_is_marked(M, I)) {\ - mark_treemap(M, I);\ - *H = X;\ - X->parent = (tchunkptr)H;\ - X->fd = X->bk = X;\ - }\ - else {\ - tchunkptr T = *H;\ - size_t K = S << leftshift_for_tree_index(I);\ - for (;;) {\ - if (chunksize(T) != S) {\ - tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ - K <<= 1;\ - if (*C != 0)\ - T = *C;\ - else if (RTCHECK(ok_address(M, C))) {\ - *C = X;\ - X->parent = T;\ - X->fd = X->bk = X;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ - }\ - }\ - else {\ - tchunkptr F = T->fd;\ - if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ - T->fd = F->bk = X;\ - X->fd = F;\ - X->bk = T;\ - X->parent = 0;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ - }\ - }\ - }\ - }\ -} - -/* - Unlink steps: - - 1. If x is a chained node, unlink it from its same-sized fd/bk links - and choose its bk node as its replacement. - 2. If x was the last node of its size, but not a leaf node, it must - be replaced with a leaf node (not merely one with an open left or - right), to make sure that lefts and rights of descendents - correspond properly to bit masks. We use the rightmost descendent - of x. We could use any other leaf, but this is easy to locate and - tends to counteract removal of leftmosts elsewhere, and so keeps - paths shorter than minimally guaranteed. This doesn't loop much - because on average a node in a tree is near the bottom. - 3. If x is the base of a chain (i.e., has parent links) relink - x's parent and children to x's replacement (or null if none). -*/ - -#define unlink_large_chunk(M, X) {\ - tchunkptr XP = X->parent;\ - tchunkptr R;\ - if (X->bk != X) {\ - tchunkptr F = X->fd;\ - R = X->bk;\ - if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ - F->bk = R;\ - R->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else {\ - tchunkptr* RP;\ - if (((R = *(RP = &(X->child[1]))) != 0) ||\ - ((R = *(RP = &(X->child[0]))) != 0)) {\ - tchunkptr* CP;\ - while ((*(CP = &(R->child[1])) != 0) ||\ - (*(CP = &(R->child[0])) != 0)) {\ - R = *(RP = CP);\ - }\ - if (RTCHECK(ok_address(M, RP)))\ - *RP = 0;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - }\ - if (XP != 0) {\ - tbinptr* H = treebin_at(M, X->index);\ - if (X == *H) {\ - if ((*H = R) == 0) \ - clear_treemap(M, X->index);\ - }\ - else if (RTCHECK(ok_address(M, XP))) {\ - if (XP->child[0] == X) \ - XP->child[0] = R;\ - else \ - XP->child[1] = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - if (R != 0) {\ - if (RTCHECK(ok_address(M, R))) {\ - tchunkptr C0, C1;\ - R->parent = XP;\ - if ((C0 = X->child[0]) != 0) {\ - if (RTCHECK(ok_address(M, C0))) {\ - R->child[0] = C0;\ - C0->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - if ((C1 = X->child[1]) != 0) {\ - if (RTCHECK(ok_address(M, C1))) {\ - R->child[1] = C1;\ - C1->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ -} - -/* Relays to large vs small bin operations */ - -#define insert_chunk(M, P, S)\ - if (is_small(S)) insert_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } - -#define unlink_chunk(M, P, S)\ - if (is_small(S)) unlink_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } - - -/* Relays to internal calls to malloc/free from realloc, memalign etc */ - -#if ONLY_MSPACES -#define internal_malloc(m, b) mspace_malloc(m, b) -#define internal_free(m, mem) mspace_free(m,mem); -#else /* ONLY_MSPACES */ -#if MSPACES -#define internal_malloc(m, b)\ - ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) -#define internal_free(m, mem)\ - if (m == gm) dlfree(mem); else mspace_free(m,mem); -#else /* MSPACES */ -#define internal_malloc(m, b) dlmalloc(b) -#define internal_free(m, mem) dlfree(mem) -#endif /* MSPACES */ -#endif /* ONLY_MSPACES */ - -/* ----------------------- Direct-mmapping chunks ----------------------- */ - -/* - Directly mmapped chunks are set up with an offset to the start of - the mmapped region stored in the prev_foot field of the chunk. This - allows reconstruction of the required argument to MUNMAP when freed, - and also allows adjustment of the returned chunk to meet alignment - requirements (especially in memalign). -*/ - -/* Malloc using mmap */ -static void* mmap_alloc(mstate m, size_t nb) { - size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - if (m->footprint_limit != 0) { - size_t fp = m->footprint + mmsize; - if (fp <= m->footprint || fp > m->footprint_limit) - return 0; - } - if (mmsize > nb) { /* Check for wrap around 0 */ - char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); - if (mm != CMFAIL) { - size_t offset = align_offset(chunk2mem(mm)); - size_t psize = mmsize - offset - MMAP_FOOT_PAD; - mchunkptr p = (mchunkptr)(mm + offset); - p->prev_foot = offset; - p->head = psize; - mark_inuse_foot(m, p, psize); - chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; - - if (m->least_addr == 0 || mm < m->least_addr) - m->least_addr = mm; - if ((m->footprint += mmsize) > m->max_footprint) - m->max_footprint = m->footprint; - assert(is_aligned(chunk2mem(p))); - check_mmapped_chunk(m, p); - return chunk2mem(p); - } - } - return 0; -} - -/* Realloc using mmap */ -static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { - size_t oldsize = chunksize(oldp); - (void)flags; /* placate people compiling -Wunused */ - if (is_small(nb)) /* Can't shrink mmap regions below small size */ - return 0; - /* Keep old chunk if big enough but not too big */ - if (oldsize >= nb + SIZE_T_SIZE && - (oldsize - nb) <= (mparams.granularity << 1)) - return oldp; - else { - size_t offset = oldp->prev_foot; - size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; - size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - char* cp = (char*)CALL_MREMAP((char*)oldp - offset, - oldmmsize, newmmsize, flags); - if (cp != CMFAIL) { - mchunkptr newp = (mchunkptr)(cp + offset); - size_t psize = newmmsize - offset - MMAP_FOOT_PAD; - newp->head = psize; - mark_inuse_foot(m, newp, psize); - chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; - - if (cp < m->least_addr) - m->least_addr = cp; - if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) - m->max_footprint = m->footprint; - check_mmapped_chunk(m, newp); - return newp; - } - } - return 0; -} - - -/* -------------------------- mspace management -------------------------- */ - -/* Initialize top chunk and its size */ -static void init_top(mstate m, mchunkptr p, size_t psize) { - /* Ensure alignment */ - size_t offset = align_offset(chunk2mem(p)); - p = (mchunkptr)((char*)p + offset); - psize -= offset; - - m->top = p; - m->topsize = psize; - p->head = psize | PINUSE_BIT; - /* set size of fake trailing chunk holding overhead space only once */ - chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; - m->trim_check = mparams.trim_threshold; /* reset on each update */ -} - -/* Initialize bins for a new mstate that is otherwise zeroed out */ -static void init_bins(mstate m) { - /* Establish circular links for smallbins */ - bindex_t i; - for (i = 0; i < NSMALLBINS; ++i) { - sbinptr bin = smallbin_at(m,i); - bin->fd = bin->bk = bin; - } -} - -#if PROCEED_ON_ERROR - -/* default corruption action */ -static void reset_on_error(mstate m) { - int i; - ++malloc_corruption_error_count; - /* Reinitialize fields to forget about all memory */ - m->smallmap = m->treemap = 0; - m->dvsize = m->topsize = 0; - m->seg.base = 0; - m->seg.size = 0; - m->seg.next = 0; - m->top = m->dv = 0; - for (i = 0; i < NTREEBINS; ++i) - *treebin_at(m, i) = 0; - init_bins(m); -} -#endif /* PROCEED_ON_ERROR */ - -/* Allocate chunk and prepend remainder with chunk in successor base. */ -static void* prepend_alloc(mstate m, char* newbase, char* oldbase, - size_t nb) { - mchunkptr p = align_as_chunk(newbase); - mchunkptr oldfirst = align_as_chunk(oldbase); - size_t psize = (char*)oldfirst - (char*)p; - mchunkptr q = chunk_plus_offset(p, nb); - size_t qsize = psize - nb; - set_size_and_pinuse_of_inuse_chunk(m, p, nb); - - assert((char*)oldfirst > (char*)q); - assert(pinuse(oldfirst)); - assert(qsize >= MIN_CHUNK_SIZE); - - /* consolidate remainder with first chunk of old base */ - if (oldfirst == m->top) { - size_t tsize = m->topsize += qsize; - m->top = q; - q->head = tsize | PINUSE_BIT; - check_top_chunk(m, q); - } - else if (oldfirst == m->dv) { - size_t dsize = m->dvsize += qsize; - m->dv = q; - set_size_and_pinuse_of_free_chunk(q, dsize); - } - else { - if (!is_inuse(oldfirst)) { - size_t nsize = chunksize(oldfirst); - unlink_chunk(m, oldfirst, nsize); - oldfirst = chunk_plus_offset(oldfirst, nsize); - qsize += nsize; - } - set_free_with_pinuse(q, qsize, oldfirst); - insert_chunk(m, q, qsize); - check_free_chunk(m, q); - } - - check_malloced_chunk(m, chunk2mem(p), nb); - return chunk2mem(p); -} - -/* Add a segment to hold a new noncontiguous region */ -static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { - /* Determine locations and sizes of segment, fenceposts, old top */ - char* old_top = (char*)m->top; - msegmentptr oldsp = segment_holding(m, old_top); - char* old_end = oldsp->base + oldsp->size; - size_t ssize = pad_request(sizeof(struct malloc_segment)); - char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - size_t offset = align_offset(chunk2mem(rawsp)); - char* asp = rawsp + offset; - char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; - mchunkptr sp = (mchunkptr)csp; - msegmentptr ss = (msegmentptr)(chunk2mem(sp)); - mchunkptr tnext = chunk_plus_offset(sp, ssize); - mchunkptr p = tnext; - int nfences = 0; - - /* reset top to new space */ - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - - /* Set up segment record */ - assert(is_aligned(ss)); - set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); - *ss = m->seg; /* Push current record */ - m->seg.base = tbase; - m->seg.size = tsize; - m->seg.sflags = mmapped; - m->seg.next = ss; - - /* Insert trailing fenceposts */ - for (;;) { - mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); - p->head = FENCEPOST_HEAD; - ++nfences; - if ((char*)(&(nextp->head)) < old_end) - p = nextp; - else - break; - } - assert(nfences >= 2); - - /* Insert the rest of old top into a bin as an ordinary free chunk */ - if (csp != old_top) { - mchunkptr q = (mchunkptr)old_top; - size_t psize = csp - old_top; - mchunkptr tn = chunk_plus_offset(q, psize); - set_free_with_pinuse(q, psize, tn); - insert_chunk(m, q, psize); - } - - check_top_chunk(m, m->top); -} - -/* -------------------------- System allocation -------------------------- */ - -/* Get memory from system using MORECORE or MMAP */ -static void* sys_alloc(mstate m, size_t nb) { - char* tbase = CMFAIL; - size_t tsize = 0; - flag_t mmap_flag = 0; - size_t asize; /* allocation size */ - - ensure_initialization(); - - /* Directly map large chunks, but only if already initialized */ - if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { - void* mem = mmap_alloc(m, nb); - if (mem != 0) - return mem; - } - - asize = granularity_align(nb + SYS_ALLOC_PADDING); - if (asize <= nb) - return 0; /* wraparound */ - if (m->footprint_limit != 0) { - size_t fp = m->footprint + asize; - if (fp <= m->footprint || fp > m->footprint_limit) - return 0; - } - - /* - Try getting memory in any of three ways (in most-preferred to - least-preferred order): - 1. A call to MORECORE that can normally contiguously extend memory. - (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or - or main space is mmapped or a previous contiguous call failed) - 2. A call to MMAP new space (disabled if not HAVE_MMAP). - Note that under the default settings, if MORECORE is unable to - fulfill a request, and HAVE_MMAP is true, then mmap is - used as a noncontiguous system allocator. This is a useful backup - strategy for systems with holes in address spaces -- in this case - sbrk cannot contiguously expand the heap, but mmap may be able to - find space. - 3. A call to MORECORE that cannot usually contiguously extend memory. - (disabled if not HAVE_MORECORE) - - In all cases, we need to request enough bytes from system to ensure - we can malloc nb bytes upon success, so pad with enough space for - top_foot, plus alignment-pad to make sure we don't lose bytes if - not on boundary, and round this up to a granularity unit. - */ - - if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { - char* br = CMFAIL; - size_t ssize = asize; /* sbrk call size */ - msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); - ACQUIRE_MALLOC_GLOBAL_LOCK(); - - if (ss == 0) { /* First time through or recovery */ - char* base = (char*)CALL_MORECORE(0); - if (base != CMFAIL) { - size_t fp; - /* Adjust to end on a page boundary */ - if (!is_page_aligned(base)) - ssize += (page_align((size_t)base) - (size_t)base); - fp = m->footprint + ssize; /* recheck limits */ - if (ssize > nb && ssize < HALF_MAX_SIZE_T && - (m->footprint_limit == 0 || - (fp > m->footprint && fp <= m->footprint_limit)) && - (br = (char*)(CALL_MORECORE(ssize))) == base) { - tbase = base; - tsize = ssize; - } - } - } - else { - /* Subtract out existing available top space from MORECORE request. */ - ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); - /* Use mem here only if it did continuously extend old space */ - if (ssize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { - tbase = br; - tsize = ssize; - } - } - - if (tbase == CMFAIL) { /* Cope with partial failure */ - if (br != CMFAIL) { /* Try to use/extend the space we did get */ - if (ssize < HALF_MAX_SIZE_T && - ssize < nb + SYS_ALLOC_PADDING) { - size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); - if (esize < HALF_MAX_SIZE_T) { - char* end = (char*)CALL_MORECORE(esize); - if (end != CMFAIL) - ssize += esize; - else { /* Can't use; try to release */ - (void) CALL_MORECORE(-ssize); - br = CMFAIL; - } - } - } - } - if (br != CMFAIL) { /* Use the space we did get */ - tbase = br; - tsize = ssize; - } - else - disable_contiguous(m); /* Don't try contiguous path in the future */ - } - - RELEASE_MALLOC_GLOBAL_LOCK(); - } - - if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ - char* mp = (char*)(CALL_MMAP(asize)); - if (mp != CMFAIL) { - tbase = mp; - tsize = asize; - mmap_flag = USE_MMAP_BIT; - } - } - - if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ - if (asize < HALF_MAX_SIZE_T) { - char* br = CMFAIL; - char* end = CMFAIL; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - br = (char*)(CALL_MORECORE(asize)); - end = (char*)(CALL_MORECORE(0)); - RELEASE_MALLOC_GLOBAL_LOCK(); - if (br != CMFAIL && end != CMFAIL && br < end) { - size_t ssize = end - br; - if (ssize > nb + TOP_FOOT_SIZE) { - tbase = br; - tsize = ssize; - } - } - } - } - - if (tbase != CMFAIL) { - - if ((m->footprint += tsize) > m->max_footprint) - m->max_footprint = m->footprint; - - if (!is_initialized(m)) { /* first-time initialization */ - if (m->least_addr == 0 || tbase < m->least_addr) - m->least_addr = tbase; - m->seg.base = tbase; - m->seg.size = tsize; - m->seg.sflags = mmap_flag; - m->magic = mparams.magic; - m->release_checks = MAX_RELEASE_CHECK_RATE; - init_bins(m); -#if !ONLY_MSPACES - if (is_global(m)) - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - else -#endif - { - /* Offset top by embedded malloc_state */ - mchunkptr mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); - } - } - - else { - /* Try to merge with an existing segment */ - msegmentptr sp = &m->seg; - /* Only consider most recent segment if traversal suppressed */ - while (sp != 0 && tbase != sp->base + sp->size) - sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & USE_MMAP_BIT) == mmap_flag && - segment_holds(sp, m->top)) { /* append */ - sp->size += tsize; - init_top(m, m->top, m->topsize + tsize); - } - else { - if (tbase < m->least_addr) - m->least_addr = tbase; - sp = &m->seg; - while (sp != 0 && sp->base != tbase + tsize) - sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & USE_MMAP_BIT) == mmap_flag) { - char* oldbase = sp->base; - sp->base = tbase; - sp->size += tsize; - return prepend_alloc(m, tbase, oldbase, nb); - } - else - add_segment(m, tbase, tsize, mmap_flag); - } - } - - if (nb < m->topsize) { /* Allocate from new or extended top space */ - size_t rsize = m->topsize -= nb; - mchunkptr p = m->top; - mchunkptr r = m->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(m, p, nb); - check_top_chunk(m, m->top); - check_malloced_chunk(m, chunk2mem(p), nb); - return chunk2mem(p); - } - } - - MALLOC_FAILURE_ACTION; - return 0; -} - -/* ----------------------- system deallocation -------------------------- */ - -/* Unmap and unlink any mmapped segments that don't contain used chunks */ -static size_t release_unused_segments(mstate m) { - size_t released = 0; - int nsegs = 0; - msegmentptr pred = &m->seg; - msegmentptr sp = pred->next; - while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; - msegmentptr next = sp->next; - ++nsegs; - if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { - mchunkptr p = align_as_chunk(base); - size_t psize = chunksize(p); - /* Can unmap if first chunk holds entire segment and not pinned */ - if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { - tchunkptr tp = (tchunkptr)p; - assert(segment_holds(sp, (char*)sp)); - if (p == m->dv) { - m->dv = 0; - m->dvsize = 0; - } - else { - unlink_large_chunk(m, tp); - } - if (CALL_MUNMAP(base, size) == 0) { - released += size; - m->footprint -= size; - /* unlink obsoleted record */ - sp = pred; - sp->next = next; - } - else { /* back out if cannot unmap */ - insert_large_chunk(m, tp, psize); - } - } - } - if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ - break; - pred = sp; - sp = next; - } - /* Reset check counter */ - m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? - (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); - return released; -} - -static int sys_trim(mstate m, size_t pad) { - size_t released = 0; - ensure_initialization(); - if (pad < MAX_REQUEST && is_initialized(m)) { - pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ - - if (m->topsize > pad) { - /* Shrink top space in granularity-size units, keeping at least one */ - size_t unit = mparams.granularity; - size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - - SIZE_T_ONE) * unit; - msegmentptr sp = segment_holding(m, (char*)m->top); - - if (!is_extern_segment(sp)) { - if (is_mmapped_segment(sp)) { - if (HAVE_MMAP && - sp->size >= extra && - !has_segment_link(m, sp)) { /* can't shrink if pinned */ - size_t newsize = sp->size - extra; - (void)newsize; /* placate people compiling -Wunused-variable */ - /* Prefer mremap, fall back to munmap */ - if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || - (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { - released = extra; - } - } - } - else if (HAVE_MORECORE) { - if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ - extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; - ACQUIRE_MALLOC_GLOBAL_LOCK(); - { - /* Make sure end of memory is where we last set it. */ - char* old_br = (char*)(CALL_MORECORE(0)); - if (old_br == sp->base + sp->size) { - char* rel_br = (char*)(CALL_MORECORE(-extra)); - char* new_br = (char*)(CALL_MORECORE(0)); - if (rel_br != CMFAIL && new_br < old_br) - released = old_br - new_br; - } - } - RELEASE_MALLOC_GLOBAL_LOCK(); - } - } - - if (released != 0) { - sp->size -= released; - m->footprint -= released; - init_top(m, m->top, m->topsize - released); - check_top_chunk(m, m->top); - } - } - - /* Unmap any unused mmapped segments */ - if (HAVE_MMAP) - released += release_unused_segments(m); - - /* On failure, disable autotrim to avoid repeated failed future calls */ - if (released == 0 && m->topsize > m->trim_check) - m->trim_check = MAX_SIZE_T; - } - - return (released != 0)? 1 : 0; -} - -/* Consolidate and bin a chunk. Differs from exported versions - of free mainly in that the chunk need not be marked as inuse. -*/ -static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - mchunkptr prev; - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - m->footprint -= psize; - return; - } - prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ - if (p != m->dv) { - unlink_chunk(m, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - m->dvsize = psize; - set_free_with_pinuse(p, psize, next); - return; - } - } - else { - CORRUPTION_ERROR_ACTION(m); - return; - } - } - if (RTCHECK(ok_address(m, next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == m->top) { - size_t tsize = m->topsize += psize; - m->top = p; - p->head = tsize | PINUSE_BIT; - if (p == m->dv) { - m->dv = 0; - m->dvsize = 0; - } - return; - } - else if (next == m->dv) { - size_t dsize = m->dvsize += psize; - m->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - return; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(m, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == m->dv) { - m->dvsize = psize; - return; - } - } - } - else { - set_free_with_pinuse(p, psize, next); - } - insert_chunk(m, p, psize); - } - else { - CORRUPTION_ERROR_ACTION(m); - } -} - -/* ---------------------------- malloc --------------------------- */ - -/* allocate a large request from the best fitting chunk in a treebin */ -static void* tmalloc_large(mstate m, size_t nb) { - tchunkptr v = 0; - size_t rsize = -nb; /* Unsigned negation */ - tchunkptr t; - bindex_t idx; - compute_tree_index(nb, idx); - if ((t = *treebin_at(m, idx)) != 0) { - /* Traverse tree for this bin looking for node with size == nb */ - size_t sizebits = nb << leftshift_for_tree_index(idx); - tchunkptr rst = 0; /* The deepest untaken right subtree */ - for (;;) { - tchunkptr rt; - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - v = t; - if ((rsize = trem) == 0) - break; - } - rt = t->child[1]; - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - if (rt != 0 && rt != t) - rst = rt; - if (t == 0) { - t = rst; /* set t to least subtree holding sizes > nb */ - break; - } - sizebits <<= 1; - } - } - if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ - binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; - if (leftbits != 0) { - bindex_t i; - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - t = *treebin_at(m, i); - } - } - - while (t != 0) { /* find smallest of tree or subtree */ - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - t = leftmost_child(t); - } - - /* If dv is a better fit, return 0 so malloc will use it */ - if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { - if (RTCHECK(ok_address(m, v))) { /* split */ - mchunkptr r = chunk_plus_offset(v, nb); - assert(chunksize(v) == rsize + nb); - if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - insert_chunk(m, r, rsize); - } - return chunk2mem(v); - } - } - CORRUPTION_ERROR_ACTION(m); - } - return 0; -} - -/* allocate a small request from the best fitting chunk in a treebin */ -static void* tmalloc_small(mstate m, size_t nb) { - tchunkptr t, v; - size_t rsize; - bindex_t i; - binmap_t leastbit = least_bit(m->treemap); - compute_bit2idx(leastbit, i); - v = t = *treebin_at(m, i); - rsize = chunksize(t) - nb; - - while ((t = leftmost_child(t)) != 0) { - size_t trem = chunksize(t) - nb; - if (trem < rsize) { - rsize = trem; - v = t; - } - } - - if (RTCHECK(ok_address(m, v))) { - mchunkptr r = chunk_plus_offset(v, nb); - assert(chunksize(v) == rsize + nb); - if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(m, r, rsize); - } - return chunk2mem(v); - } - } - - CORRUPTION_ERROR_ACTION(m); - return 0; -} - -#if !ONLY_MSPACES - -void* dlmalloc(size_t bytes) { - /* - Basic algorithm: - If a small request (< 256 bytes minus per-chunk overhead): - 1. If one exists, use a remainderless chunk in associated smallbin. - (Remainderless means that there are too few excess bytes to - represent as a chunk.) - 2. If it is big enough, use the dv chunk, which is normally the - chunk adjacent to the one used for the most recent small request. - 3. If one exists, split the smallest available chunk in a bin, - saving remainder in dv. - 4. If it is big enough, use the top chunk. - 5. If available, get memory from system and use it - Otherwise, for a large request: - 1. Find the smallest available binned chunk that fits, and use it - if it is better fitting than dv chunk, splitting if necessary. - 2. If better fitting than any binned chunk, use the dv chunk. - 3. If it is big enough, use the top chunk. - 4. If request size >= mmap threshold, try to directly mmap this chunk. - 5. If available, get memory from system and use it - - The ugly goto's here ensure that postaction occurs along all paths. - */ - -#if USE_LOCKS - ensure_initialization(); /* initialize in sys_alloc if not using locks */ -#endif - - if (!PREACTION(gm)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = gm->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(gm, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(gm, b, p, idx); - set_inuse_and_pinuse(gm, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb > gm->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(gm, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(gm, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(gm, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(gm, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - - if (nb <= gm->dvsize) { - size_t rsize = gm->dvsize - nb; - mchunkptr p = gm->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = gm->dv = chunk_plus_offset(p, nb); - gm->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - } - else { /* exhaust dv */ - size_t dvs = gm->dvsize; - gm->dvsize = 0; - gm->dv = 0; - set_inuse_and_pinuse(gm, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb < gm->topsize) { /* Split top */ - size_t rsize = gm->topsize -= nb; - mchunkptr p = gm->top; - mchunkptr r = gm->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - mem = chunk2mem(p); - check_top_chunk(gm, gm->top); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - mem = sys_alloc(gm, nb); - - postaction: - POSTACTION(gm); - return mem; - } - - return 0; -} - -/* ---------------------------- free --------------------------- */ - -void dlfree(void* mem) { - /* - Consolidate freed chunks with preceeding or succeeding bordering - free chunks, if they exist, and then place in a bin. Intermixed - with special cases for top, dv, mmapped chunks, and usage errors. - */ - - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } -#else /* FOOTERS */ -#define fm gm -#endif /* FOOTERS */ - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - - if (is_small(psize)) { - insert_small_chunk(fm, p, psize); - check_free_chunk(fm, p); - } - else { - tchunkptr tp = (tchunkptr)p; - insert_large_chunk(fm, tp, psize); - check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); - } - goto postaction; - } - } - erroraction: - USAGE_ERROR_ACTION(fm, p); - postaction: - POSTACTION(fm); - } - } -#if !FOOTERS -#undef fm -#endif /* FOOTERS */ -} - -void* dlcalloc(size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = dlmalloc(req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - -#endif /* !ONLY_MSPACES */ - -/* ------------ Internal support for realloc, memalign, etc -------------- */ - -/* Try to realloc; only in-place unless can_move true */ -static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, - int can_move) { - mchunkptr newp = 0; - size_t oldsize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, oldsize); - if (RTCHECK(ok_address(m, p) && ok_inuse(p) && - ok_next(p, next) && ok_pinuse(next))) { - if (is_mmapped(p)) { - newp = mmap_resize(m, p, nb, can_move); - } - else if (oldsize >= nb) { /* already big enough */ - size_t rsize = oldsize - nb; - if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ - mchunkptr r = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - set_inuse(m, r, rsize); - dispose_chunk(m, r, rsize); - } - newp = p; - } - else if (next == m->top) { /* extend into top */ - if (oldsize + m->topsize > nb) { - size_t newsize = oldsize + m->topsize; - size_t newtopsize = newsize - nb; - mchunkptr newtop = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - newtop->head = newtopsize |PINUSE_BIT; - m->top = newtop; - m->topsize = newtopsize; - newp = p; - } - } - else if (next == m->dv) { /* extend into dv */ - size_t dvs = m->dvsize; - if (oldsize + dvs >= nb) { - size_t dsize = oldsize + dvs - nb; - if (dsize >= MIN_CHUNK_SIZE) { - mchunkptr r = chunk_plus_offset(p, nb); - mchunkptr n = chunk_plus_offset(r, dsize); - set_inuse(m, p, nb); - set_size_and_pinuse_of_free_chunk(r, dsize); - clear_pinuse(n); - m->dvsize = dsize; - m->dv = r; - } - else { /* exhaust dv */ - size_t newsize = oldsize + dvs; - set_inuse(m, p, newsize); - m->dvsize = 0; - m->dv = 0; - } - newp = p; - } - } - else if (!cinuse(next)) { /* extend into next free chunk */ - size_t nextsize = chunksize(next); - if (oldsize + nextsize >= nb) { - size_t rsize = oldsize + nextsize - nb; - unlink_chunk(m, next, nextsize); - if (rsize < MIN_CHUNK_SIZE) { - size_t newsize = oldsize + nextsize; - set_inuse(m, p, newsize); - } - else { - mchunkptr r = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - set_inuse(m, r, rsize); - dispose_chunk(m, r, rsize); - } - newp = p; - } - } - } - else { - USAGE_ERROR_ACTION(m, chunk2mem(p)); - } - return newp; -} - -static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { - void* mem = 0; - if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ - alignment = MIN_CHUNK_SIZE; - if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ - size_t a = MALLOC_ALIGNMENT << 1; - while (a < alignment) a <<= 1; - alignment = a; - } - if (bytes >= MAX_REQUEST - alignment) { - if (m != 0) { /* Test isn't needed but avoids compiler warning */ - MALLOC_FAILURE_ACTION; - } - } - else { - size_t nb = request2size(bytes); - size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; - mem = internal_malloc(m, req); - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (PREACTION(m)) - return 0; - if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ - /* - Find an aligned spot inside chunk. Since we need to give - back leading space in a chunk of at least MIN_CHUNK_SIZE, if - the first calculation places us at a spot with less than - MIN_CHUNK_SIZE leader, we can move to the next aligned spot. - We've allocated enough total room so that this is always - possible. - */ - char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - - SIZE_T_ONE)) & - -alignment)); - char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? - br : br+alignment; - mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char*)(p); - size_t newsize = chunksize(p) - leadsize; - - if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ - newp->prev_foot = p->prev_foot + leadsize; - newp->head = newsize; - } - else { /* Otherwise, give back leader, use the rest */ - set_inuse(m, newp, newsize); - set_inuse(m, p, leadsize); - dispose_chunk(m, p, leadsize); - } - p = newp; - } - - /* Give back spare room at the end */ - if (!is_mmapped(p)) { - size_t size = chunksize(p); - if (size > nb + MIN_CHUNK_SIZE) { - size_t remainder_size = size - nb; - mchunkptr remainder = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - set_inuse(m, remainder, remainder_size); - dispose_chunk(m, remainder, remainder_size); - } - } - - mem = chunk2mem(p); - assert (chunksize(p) >= nb); - assert(((size_t)mem & (alignment - 1)) == 0); - check_inuse_chunk(m, p); - POSTACTION(m); - } - } - return mem; -} - -/* - Common support for independent_X routines, handling - all of the combinations that can result. - The opts arg has: - bit 0 set if all elements are same size (using sizes[0]) - bit 1 set if elements should be zeroed -*/ -static void** ialloc(mstate m, - size_t n_elements, - size_t* sizes, - int opts, - void* chunks[]) { - - size_t element_size; /* chunksize of each element, if all same */ - size_t contents_size; /* total size of elements */ - size_t array_size; /* request size of pointer array */ - void* mem; /* malloced aggregate space */ - mchunkptr p; /* corresponding chunk */ - size_t remainder_size; /* remaining bytes while splitting */ - void** marray; /* either "chunks" or malloced ptr array */ - mchunkptr array_chunk; /* chunk for malloced ptr array */ - flag_t was_enabled; /* to disable mmap */ - size_t size; - size_t i; - - ensure_initialization(); - /* compute array length, if needed */ - if (chunks != 0) { - if (n_elements == 0) - return chunks; /* nothing to do */ - marray = chunks; - array_size = 0; - } - else { - /* if empty req, must still return chunk representing empty array */ - if (n_elements == 0) - return (void**)internal_malloc(m, 0); - marray = 0; - array_size = request2size(n_elements * (sizeof(void*))); - } - - /* compute total element size */ - if (opts & 0x1) { /* all-same-size */ - element_size = request2size(*sizes); - contents_size = n_elements * element_size; - } - else { /* add up all the sizes */ - element_size = 0; - contents_size = 0; - for (i = 0; i != n_elements; ++i) - contents_size += request2size(sizes[i]); - } - - size = contents_size + array_size; - - /* - Allocate the aggregate chunk. First disable direct-mmapping so - malloc won't use it, since we would not be able to later - free/realloc space internal to a segregated mmap region. - */ - was_enabled = use_mmap(m); - disable_mmap(m); - mem = internal_malloc(m, size - CHUNK_OVERHEAD); - if (was_enabled) - enable_mmap(m); - if (mem == 0) - return 0; - - if (PREACTION(m)) return 0; - p = mem2chunk(mem); - remainder_size = chunksize(p); - - assert(!is_mmapped(p)); - - if (opts & 0x2) { /* optionally clear the elements */ - memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); - } - - /* If not provided, allocate the pointer array as final part of chunk */ - if (marray == 0) { - size_t array_chunk_size; - array_chunk = chunk_plus_offset(p, contents_size); - array_chunk_size = remainder_size - contents_size; - marray = (void**) (chunk2mem(array_chunk)); - set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); - remainder_size = contents_size; - } - - /* split out elements */ - for (i = 0; ; ++i) { - marray[i] = chunk2mem(p); - if (i != n_elements-1) { - if (element_size != 0) - size = element_size; - else - size = request2size(sizes[i]); - remainder_size -= size; - set_size_and_pinuse_of_inuse_chunk(m, p, size); - p = chunk_plus_offset(p, size); - } - else { /* the final element absorbs any overallocation slop */ - set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); - break; - } - } - -#if DEBUG - if (marray != chunks) { - /* final element must have exactly exhausted chunk */ - if (element_size != 0) { - assert(remainder_size == element_size); - } - else { - assert(remainder_size == request2size(sizes[i])); - } - check_inuse_chunk(m, mem2chunk(marray)); - } - for (i = 0; i != n_elements; ++i) - check_inuse_chunk(m, mem2chunk(marray[i])); - -#endif /* DEBUG */ - - POSTACTION(m); - return marray; -} - -/* Try to free all pointers in the given array. - Note: this could be made faster, by delaying consolidation, - at the price of disabling some user integrity checks, We - still optimize some consolidations by combining adjacent - chunks before freeing, which will occur often if allocated - with ialloc or the array is sorted. -*/ -static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { - size_t unfreed = 0; - if (!PREACTION(m)) { - void** a; - void** fence = &(array[nelem]); - for (a = array; a != fence; ++a) { - void* mem = *a; - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - size_t psize = chunksize(p); -#if FOOTERS - if (get_mstate_for(p) != m) { - ++unfreed; - continue; - } -#endif - check_inuse_chunk(m, p); - *a = 0; - if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { - void ** b = a + 1; /* try to merge with next chunk */ - mchunkptr next = next_chunk(p); - if (b != fence && *b == chunk2mem(next)) { - size_t newsize = chunksize(next) + psize; - set_inuse(m, p, newsize); - *b = chunk2mem(p); - } - else - dispose_chunk(m, p, psize); - } - else { - CORRUPTION_ERROR_ACTION(m); - break; - } - } - } - if (should_trim(m, m->topsize)) - sys_trim(m, 0); - POSTACTION(m); - } - return unfreed; -} - -/* Traversal */ -#if MALLOC_INSPECT_ALL -static void internal_inspect_all(mstate m, - void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg) { - if (is_initialized(m)) { - mchunkptr top = m->top; - msegmentptr s; - for (s = &m->seg; s != 0; s = s->next) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { - mchunkptr next = next_chunk(q); - size_t sz = chunksize(q); - size_t used; - void* start; - if (is_inuse(q)) { - used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ - start = chunk2mem(q); - } - else { - used = 0; - if (is_small(sz)) { /* offset by possible bookkeeping */ - start = (void*)((char*)q + sizeof(struct malloc_chunk)); - } - else { - start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); - } - } - if (start < (void*)next) /* skip if all space is bookkeeping */ - handler(start, next, used, arg); - if (q == top) - break; - q = next; - } - } - } -} -#endif /* MALLOC_INSPECT_ALL */ - -/* ------------------ Exported realloc, memalign, etc -------------------- */ - -#if !ONLY_MSPACES - -void* dlrealloc(void* oldmem, size_t bytes) { - void* mem = 0; - if (oldmem == 0) { - mem = dlmalloc(bytes); - } - else if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - } -#ifdef REALLOC_ZERO_BYTES_FREES - else if (bytes == 0) { - dlfree(oldmem); - } -#endif /* REALLOC_ZERO_BYTES_FREES */ - else { - size_t nb = request2size(bytes); - mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS - mstate m = gm; -#else /* FOOTERS */ - mstate m = get_mstate_for(oldp); - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } -#endif /* FOOTERS */ - if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); - POSTACTION(m); - if (newp != 0) { - check_inuse_chunk(m, newp); - mem = chunk2mem(newp); - } - else { - mem = internal_malloc(m, bytes); - if (mem != 0) { - size_t oc = chunksize(oldp) - overhead_for(oldp); - memcpy(mem, oldmem, (oc < bytes)? oc : bytes); - internal_free(m, oldmem); - } - } - } - } - return mem; -} - -void* dlrealloc_in_place(void* oldmem, size_t bytes) { - void* mem = 0; - if (oldmem != 0) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - } - else { - size_t nb = request2size(bytes); - mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS - mstate m = gm; -#else /* FOOTERS */ - mstate m = get_mstate_for(oldp); - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } -#endif /* FOOTERS */ - if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); - POSTACTION(m); - if (newp == oldp) { - check_inuse_chunk(m, newp); - mem = oldmem; - } - } - } - } - return mem; -} - -void* dlmemalign(size_t alignment, size_t bytes) { - if (alignment <= MALLOC_ALIGNMENT) { - return dlmalloc(bytes); - } - return internal_memalign(gm, alignment, bytes); -} - -int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { - void* mem = 0; - if (alignment == MALLOC_ALIGNMENT) - mem = dlmalloc(bytes); - else { - size_t d = alignment / sizeof(void*); - size_t r = alignment % sizeof(void*); - if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) - return EINVAL; - else if (bytes <= MAX_REQUEST - alignment) { - if (alignment < MIN_CHUNK_SIZE) - alignment = MIN_CHUNK_SIZE; - mem = internal_memalign(gm, alignment, bytes); - } - } - if (mem == 0) - return ENOMEM; - else { - *pp = mem; - return 0; - } -} - -void* dlvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = mparams.page_size; - return dlmemalign(pagesz, bytes); -} - -void* dlpvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = mparams.page_size; - return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); -} - -void** dlindependent_calloc(size_t n_elements, size_t elem_size, - void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - return ialloc(gm, n_elements, &sz, 3, chunks); -} - -void** dlindependent_comalloc(size_t n_elements, size_t sizes[], - void* chunks[]) { - return ialloc(gm, n_elements, sizes, 0, chunks); -} - -size_t dlbulk_free(void* array[], size_t nelem) { - return internal_bulk_free(gm, array, nelem); -} - -#if MALLOC_INSPECT_ALL -void dlmalloc_inspect_all(void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg) { - ensure_initialization(); - if (!PREACTION(gm)) { - internal_inspect_all(gm, handler, arg); - POSTACTION(gm); - } -} -#endif /* MALLOC_INSPECT_ALL */ - -int dlmalloc_trim(size_t pad) { - int result = 0; - ensure_initialization(); - if (!PREACTION(gm)) { - result = sys_trim(gm, pad); - POSTACTION(gm); - } - return result; -} - -size_t dlmalloc_footprint(void) { - return gm->footprint; -} - -size_t dlmalloc_max_footprint(void) { - return gm->max_footprint; -} - -size_t dlmalloc_footprint_limit(void) { - size_t maf = gm->footprint_limit; - return maf == 0 ? MAX_SIZE_T : maf; -} - -size_t dlmalloc_set_footprint_limit(size_t bytes) { - size_t result; /* invert sense of 0 */ - if (bytes == 0) - result = granularity_align(1); /* Use minimal size */ - if (bytes == MAX_SIZE_T) - result = 0; /* disable */ - else - result = granularity_align(bytes); - return gm->footprint_limit = result; -} - -#if !NO_MALLINFO -struct mallinfo dlmallinfo(void) { - return internal_mallinfo(gm); -} -#endif /* NO_MALLINFO */ - -#if !NO_MALLOC_STATS -void dlmalloc_stats() { - internal_malloc_stats(gm); -} -#endif /* NO_MALLOC_STATS */ - -int dlmallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - -size_t dlmalloc_usable_size(void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -#endif /* !ONLY_MSPACES */ - -/* ----------------------------- user mspaces ---------------------------- */ - -#if MSPACES - -static mstate init_user_mstate(char* tbase, size_t tsize) { - size_t msize = pad_request(sizeof(struct malloc_state)); - mchunkptr mn; - mchunkptr msp = align_as_chunk(tbase); - mstate m = (mstate)(chunk2mem(msp)); - memset(m, 0, msize); - (void)INITIAL_LOCK(&m->mutex); - msp->head = (msize|INUSE_BITS); - m->seg.base = m->least_addr = tbase; - m->seg.size = m->footprint = m->max_footprint = tsize; - m->magic = mparams.magic; - m->release_checks = MAX_RELEASE_CHECK_RATE; - m->mflags = mparams.default_mflags; - m->extp = 0; - m->exts = 0; - disable_contiguous(m); - init_bins(m); - mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); - check_top_chunk(m, m->top); - return m; -} - -mspace create_mspace(size_t capacity, int locked) { - mstate m = 0; - size_t msize; - ensure_initialization(); - msize = pad_request(sizeof(struct malloc_state)); - if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - size_t rs = ((capacity == 0)? mparams.granularity : - (capacity + TOP_FOOT_SIZE + msize)); - size_t tsize = granularity_align(rs); - char* tbase = (char*)(CALL_MMAP(tsize)); - if (tbase != CMFAIL) { - m = init_user_mstate(tbase, tsize); - m->seg.sflags = USE_MMAP_BIT; - set_lock(m, locked); - } - } - return (mspace)m; -} - -mspace create_mspace_with_base(void* base, size_t capacity, int locked) { - mstate m = 0; - size_t msize; - ensure_initialization(); - msize = pad_request(sizeof(struct malloc_state)); - if (capacity > msize + TOP_FOOT_SIZE && - capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - m = init_user_mstate((char*)base, capacity); - m->seg.sflags = EXTERN_BIT; - set_lock(m, locked); - } - return (mspace)m; -} - -int mspace_track_large_chunks(mspace msp, int enable) { - int ret = 0; - mstate ms = (mstate)msp; - if (!PREACTION(ms)) { - if (!use_mmap(ms)) { - ret = 1; - } - if (!enable) { - enable_mmap(ms); - } else { - disable_mmap(ms); - } - POSTACTION(ms); - } - return ret; -} - -size_t destroy_mspace(mspace msp) { - size_t freed = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - msegmentptr sp = &ms->seg; - (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ - while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; - flag_t flag = sp->sflags; - (void)base; /* placate people compiling -Wunused-variable */ - sp = sp->next; - if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && - CALL_MUNMAP(base, size) == 0) - freed += size; - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return freed; -} - -/* - mspace versions of routines are near-clones of the global - versions. This is not so nice but better than the alternatives. -*/ - -void* mspace_malloc(mspace msp, size_t bytes) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (!PREACTION(ms)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = ms->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(ms, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(ms, b, p, idx); - set_inuse_and_pinuse(ms, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (nb > ms->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(ms, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(ms, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(ms, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(ms, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - } - - if (nb <= ms->dvsize) { - size_t rsize = ms->dvsize - nb; - mchunkptr p = ms->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = ms->dv = chunk_plus_offset(p, nb); - ms->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - } - else { /* exhaust dv */ - size_t dvs = ms->dvsize; - ms->dvsize = 0; - ms->dv = 0; - set_inuse_and_pinuse(ms, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - else if (nb < ms->topsize) { /* Split top */ - size_t rsize = ms->topsize -= nb; - mchunkptr p = ms->top; - mchunkptr r = ms->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - mem = chunk2mem(p); - check_top_chunk(ms, ms->top); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } - - mem = sys_alloc(ms, nb); - - postaction: - POSTACTION(ms); - return mem; - } - - return 0; -} - -void mspace_free(mspace msp, void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - (void)msp; /* placate people compiling -Wunused */ -#else /* FOOTERS */ - mstate fm = (mstate)msp; -#endif /* FOOTERS */ - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - - if (is_small(psize)) { - insert_small_chunk(fm, p, psize); - check_free_chunk(fm, p); - } - else { - tchunkptr tp = (tchunkptr)p; - insert_large_chunk(fm, tp, psize); - check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); - } - goto postaction; - } - } - erroraction: - USAGE_ERROR_ACTION(fm, p); - postaction: - POSTACTION(fm); - } - } -} - -void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = internal_malloc(ms, req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - -void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { - void* mem = 0; - if (oldmem == 0) { - mem = mspace_malloc(msp, bytes); - } - else if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - } -#ifdef REALLOC_ZERO_BYTES_FREES - else if (bytes == 0) { - mspace_free(msp, oldmem); - } -#endif /* REALLOC_ZERO_BYTES_FREES */ - else { - size_t nb = request2size(bytes); - mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS - mstate m = (mstate)msp; -#else /* FOOTERS */ - mstate m = get_mstate_for(oldp); - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } -#endif /* FOOTERS */ - if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); - POSTACTION(m); - if (newp != 0) { - check_inuse_chunk(m, newp); - mem = chunk2mem(newp); - } - else { - mem = mspace_malloc(m, bytes); - if (mem != 0) { - size_t oc = chunksize(oldp) - overhead_for(oldp); - memcpy(mem, oldmem, (oc < bytes)? oc : bytes); - mspace_free(m, oldmem); - } - } - } - } - return mem; -} - -void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { - void* mem = 0; - if (oldmem != 0) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - } - else { - size_t nb = request2size(bytes); - mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS - mstate m = (mstate)msp; -#else /* FOOTERS */ - mstate m = get_mstate_for(oldp); - (void)msp; /* placate people compiling -Wunused */ - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } -#endif /* FOOTERS */ - if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); - POSTACTION(m); - if (newp == oldp) { - check_inuse_chunk(m, newp); - mem = oldmem; - } - } - } - } - return mem; -} - -void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - if (alignment <= MALLOC_ALIGNMENT) - return mspace_malloc(msp, bytes); - return internal_memalign(ms, alignment, bytes); -} - -void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return ialloc(ms, n_elements, &sz, 3, chunks); -} - -void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - return 0; - } - return ialloc(ms, n_elements, sizes, 0, chunks); -} - -size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { - return internal_bulk_free((mstate)msp, array, nelem); -} - -#if MALLOC_INSPECT_ALL -void mspace_inspect_all(mspace msp, - void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg) { - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - if (!PREACTION(ms)) { - internal_inspect_all(ms, handler, arg); - POSTACTION(ms); - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } -} -#endif /* MALLOC_INSPECT_ALL */ - -int mspace_trim(mspace msp, size_t pad) { - int result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - if (!PREACTION(ms)) { - result = sys_trim(ms, pad); - POSTACTION(ms); - } - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -#if !NO_MALLOC_STATS -void mspace_malloc_stats(mspace msp) { - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - internal_malloc_stats(ms); - } - else { - USAGE_ERROR_ACTION(ms,ms); - } -} -#endif /* NO_MALLOC_STATS */ - -size_t mspace_footprint(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - result = ms->footprint; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -size_t mspace_max_footprint(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - result = ms->max_footprint; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -size_t mspace_footprint_limit(mspace msp) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - size_t maf = ms->footprint_limit; - result = (maf == 0) ? MAX_SIZE_T : maf; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { - size_t result = 0; - mstate ms = (mstate)msp; - if (ok_magic(ms)) { - if (bytes == 0) - result = granularity_align(1); /* Use minimal size */ - if (bytes == MAX_SIZE_T) - result = 0; /* disable */ - else - result = granularity_align(bytes); - ms->footprint_limit = result; - } - else { - USAGE_ERROR_ACTION(ms,ms); - } - return result; -} - -#if !NO_MALLINFO -struct mallinfo mspace_mallinfo(mspace msp) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - } - return internal_mallinfo(ms); -} -#endif /* NO_MALLINFO */ - -size_t mspace_usable_size(const void* mem) { - if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -int mspace_mallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - -#endif /* MSPACES */ - - -/* -------------------- Alternative MORECORE functions ------------------- */ - -/* - Guidelines for creating a custom version of MORECORE: - - * For best performance, MORECORE should allocate in multiples of pagesize. - * MORECORE may allocate more memory than requested. (Or even less, - but this will usually result in a malloc failure.) - * MORECORE must not allocate memory when given argument zero, but - instead return one past the end address of memory from previous - nonzero call. - * For best performance, consecutive calls to MORECORE with positive - arguments should return increasing addresses, indicating that - space has been contiguously extended. - * Even though consecutive calls to MORECORE need not return contiguous - addresses, it must be OK for malloc'ed chunks to span multiple - regions in those cases where they do happen to be contiguous. - * MORECORE need not handle negative arguments -- it may instead - just return MFAIL when given negative arguments. - Negative arguments are always multiples of pagesize. MORECORE - must not misinterpret negative args as large positive unsigned - args. You can suppress all such calls from even occurring by defining - MORECORE_CANNOT_TRIM, - - As an example alternative MORECORE, here is a custom allocator - kindly contributed for pre-OSX macOS. It uses virtually but not - necessarily physically contiguous non-paged memory (locked in, - present and won't get swapped out). You can use it by uncommenting - this section, adding some #includes, and setting up the appropriate - defines above: - - #define MORECORE osMoreCore - - There is also a shutdown routine that should somehow be called for - cleanup upon program exit. - - #define MAX_POOL_ENTRIES 100 - #define MINIMUM_MORECORE_SIZE (64 * 1024U) - static int next_os_pool; - void *our_os_pools[MAX_POOL_ENTRIES]; - - void *osMoreCore(int size) - { - void *ptr = 0; - static void *sbrk_top = 0; - - if (size > 0) - { - if (size < MINIMUM_MORECORE_SIZE) - size = MINIMUM_MORECORE_SIZE; - if (CurrentExecutionLevel() == kTaskLevel) - ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); - if (ptr == 0) - { - return (void *) MFAIL; - } - // save ptrs so they can be freed during cleanup - our_os_pools[next_os_pool] = ptr; - next_os_pool++; - ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); - sbrk_top = (char *) ptr + size; - return ptr; - } - else if (size < 0) - { - // we don't currently support shrink behavior - return (void *) MFAIL; - } - else - { - return sbrk_top; - } - } - - // cleanup any allocated memory pools - // called as last thing before shutting down driver - - void osCleanupMem(void) - { - void **ptr; - - for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) - if (*ptr) - { - PoolDeallocate(*ptr); - *ptr = 0; - } - } - -*/ - - -/* ----------------------------------------------------------------------- -History: - v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea - * fix bad comparison in dlposix_memalign - * don't reuse adjusted asize in sys_alloc - * add LOCK_AT_FORK -- thanks to Kirill Artamonov for the suggestion - * reduce compiler warnings -- thanks to all who reported/suggested these - - v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) - * Always perform unlink checks unless INSECURE - * Add posix_memalign. - * Improve realloc to expand in more cases; expose realloc_in_place. - Thanks to Peter Buhr for the suggestion. - * Add footprint_limit, inspect_all, bulk_free. Thanks - to Barry Hayes and others for the suggestions. - * Internal refactorings to avoid calls while holding locks - * Use non-reentrant locks by default. Thanks to Roland McGrath - for the suggestion. - * Small fixes to mspace_destroy, reset_on_error. - * Various configuration extensions/changes. Thanks - to all who contributed these. - - V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu) - * Update Creative Commons URL - - V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) - * Use zeros instead of prev foot for is_mmapped - * Add mspace_track_large_chunks; thanks to Jean Brouwers - * Fix set_inuse in internal_realloc; thanks to Jean Brouwers - * Fix insufficient sys_alloc padding when using 16byte alignment - * Fix bad error check in mspace_footprint - * Adaptations for ptmalloc; thanks to Wolfram Gloger. - * Reentrant spin locks; thanks to Earl Chew and others - * Win32 improvements; thanks to Niall Douglas and Earl Chew - * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options - * Extension hook in malloc_state - * Various small adjustments to reduce warnings on some compilers - * Various configuration extensions/changes for more platforms. Thanks - to all who contributed these. - - V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) - * Add max_footprint functions - * Ensure all appropriate literals are size_t - * Fix conditional compilation problem for some #define settings - * Avoid concatenating segments with the one provided - in create_mspace_with_base - * Rename some variables to avoid compiler shadowing warnings - * Use explicit lock initialization. - * Better handling of sbrk interference. - * Simplify and fix segment insertion, trimming and mspace_destroy - * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x - * Thanks especially to Dennis Flanagan for help on these. - - V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) - * Fix memalign brace error. - - V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) - * Fix improper #endif nesting in C++ - * Add explicit casts needed for C++ - - V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) - * Use trees for large bins - * Support mspaces - * Use segments to unify sbrk-based and mmap-based system allocation, - removing need for emulation on most platforms without sbrk. - * Default safety checks - * Optional footer checks. Thanks to William Robertson for the idea. - * Internal code refactoring - * Incorporate suggestions and platform-specific changes. - Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, - Aaron Bachmann, Emery Berger, and others. - * Speed up non-fastbin processing enough to remove fastbins. - * Remove useless cfree() to avoid conflicts with other apps. - * Remove internal memcpy, memset. Compilers handle builtins better. - * Remove some options that no one ever used and rename others. - - V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) - * Fix malloc_state bitmap array misdeclaration - - V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) - * Allow tuning of FIRST_SORTED_BIN_SIZE - * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. - * Better detection and support for non-contiguousness of MORECORE. - Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger - * Bypass most of malloc if no frees. Thanks To Emery Berger. - * Fix freeing of old top non-contiguous chunk im sysmalloc. - * Raised default trim and map thresholds to 256K. - * Fix mmap-related #defines. Thanks to Lubos Lunak. - * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. - * Branch-free bin calculation - * Default trim and mmap thresholds now 256K. - - V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) - * Introduce independent_comalloc and independent_calloc. - Thanks to Michael Pachos for motivation and help. - * Make optional .h file available - * Allow > 2GB requests on 32bit systems. - * new WIN32 sbrk, mmap, munmap, lock code from <[email protected]>. - Thanks also to Andreas Mueller <a.mueller at paradatec.de>, - and Anonymous. - * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for - helping test this.) - * memalign: check alignment arg - * realloc: don't try to shift chunks backwards, since this - leads to more fragmentation in some programs and doesn't - seem to help in any others. - * Collect all cases in malloc requiring system memory into sysmalloc - * Use mmap as backup to sbrk - * Place all internal state in malloc_state - * Introduce fastbins (although similar to 2.5.1) - * Many minor tunings and cosmetic improvements - * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK - * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS - Thanks to Tony E. Bennett <[email protected]> and others. - * Include errno.h to support default failure action. - - V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) - * return null for negative arguments - * Added Several WIN32 cleanups from Martin C. Fong <mcfong at yahoo.com> - * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' - (e.g. WIN32 platforms) - * Cleanup header file inclusion for WIN32 platforms - * Cleanup code to avoid Microsoft Visual C++ compiler complaints - * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing - memory allocation routines - * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) - * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to - usage of 'assert' in non-WIN32 code - * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to - avoid infinite loop - * Always call 'fREe()' rather than 'free()' - - V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) - * Fixed ordering problem with boundary-stamping - - V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) - * Added pvalloc, as recommended by H.J. Liu - * Added 64bit pointer support mainly from Wolfram Gloger - * Added anonymously donated WIN32 sbrk emulation - * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen - * malloc_extend_top: fix mask error that caused wastage after - foreign sbrks - * Add linux mremap support code from HJ Liu - - V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) - * Integrated most documentation with the code. - * Add support for mmap, with help from - Wolfram Gloger ([email protected]). - * Use last_remainder in more cases. - * Pack bins using idea from [email protected] - * Use ordered bins instead of best-fit threshhold - * Eliminate block-local decls to simplify tracing and debugging. - * Support another case of realloc via move into top - * Fix error occuring when initial sbrk_base not word-aligned. - * Rely on page size for units instead of SBRK_UNIT to - avoid surprises about sbrk alignment conventions. - * Add mallinfo, mallopt. Thanks to Raymond Nijssen - ([email protected]) for the suggestion. - * Add `pad' argument to malloc_trim and top_pad mallopt parameter. - * More precautions for cases where other routines call sbrk, - courtesy of Wolfram Gloger ([email protected]). - * Added macros etc., allowing use in linux libc from - H.J. Lu ([email protected]) - * Inverted this history list - - V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) - * Re-tuned and fixed to behave more nicely with V2.6.0 changes. - * Removed all preallocation code since under current scheme - the work required to undo bad preallocations exceeds - the work saved in good cases for most test programs. - * No longer use return list or unconsolidated bins since - no scheme using them consistently outperforms those that don't - given above changes. - * Use best fit for very large chunks to prevent some worst-cases. - * Added some support for debugging - - V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) - * Removed footers when chunks are in use. Thanks to - Paul Wilson ([email protected]) for the suggestion. - - V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) - * Added malloc_trim, with help from Wolfram Gloger - ([email protected]). - - V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) - - V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) - * realloc: try to expand in both directions - * malloc: swap order of clean-bin strategy; - * realloc: only conditionally expand backwards - * Try not to scavenge used bins - * Use bin counts as a guide to preallocation - * Occasionally bin return list chunks in first scan - * Add a few optimizations from [email protected] - - V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) - * faster bin computation & slightly different binning - * merged all consolidations to one part of malloc proper - (eliminating old malloc_find_space & malloc_clean_bin) - * Scan 2 returns chunks (not just 1) - * Propagate failure in realloc if malloc returns 0 - * Add stuff to allow compilation on non-ANSI compilers - from [email protected] - - V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) - * removed potential for odd address access in prev_chunk - * removed dependency on getpagesize.h - * misc cosmetics and a bit more internal documentation - * anticosmetics: mangled names in macros to evade debugger strangeness - * tested on sparc, hp-700, dec-mips, rs6000 - with gcc & native cc (hp, dec only) allowing - Detlefs & Zorn comparison study (in SIGPLAN Notices.) - - Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) - * Based loosely on libg++-1.2X malloc. (It retains some of the overall - structure of old version, but most details differ.) - -*/ diff --git a/src/user/lib/vendor/dlmalloc/malloc.h b/src/user/lib/vendor/dlmalloc/malloc.h deleted file mode 100644 index e52c9e5..0000000 --- a/src/user/lib/vendor/dlmalloc/malloc.h +++ /dev/null @@ -1,620 +0,0 @@ -/* - Default header file for malloc-2.8.x, written by Doug Lea - and released to the public domain, as explained at - http://creativecommons.org/publicdomain/zero/1.0/ - - This header is for ANSI C/C++ only. You can set any of - the following #defines before including: - - * If USE_DL_PREFIX is defined, it is assumed that malloc.c - was also compiled with this option, so all routines - have names starting with "dl". - - * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this - file will be #included AFTER <malloc.h>. This is needed only if - your system defines a struct mallinfo that is incompatible with the - standard one declared here. Otherwise, you can include this file - INSTEAD of your system system <malloc.h>. At least on ANSI, all - declarations should be compatible with system versions - - * If MSPACES is defined, declarations for mspace versions are included. -*/ - -#ifndef MALLOC_280_H -#define MALLOC_280_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stddef.h> /* for size_t */ - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 /* define to a value */ -#elif ONLY_MSPACES != 0 -#define ONLY_MSPACES 1 -#endif /* ONLY_MSPACES */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ - -#ifndef MSPACES -#if ONLY_MSPACES -#define MSPACES 1 -#else /* ONLY_MSPACES */ -#define MSPACES 0 -#endif /* ONLY_MSPACES */ -#endif /* MSPACES */ - -#if !ONLY_MSPACES - -#ifndef USE_DL_PREFIX -#define dlcalloc calloc -#define dlfree free -#define dlmalloc malloc -#define dlmemalign memalign -#define dlposix_memalign posix_memalign -#define dlrealloc realloc -#define dlvalloc valloc -#define dlpvalloc pvalloc -#define dlmallinfo mallinfo -#define dlmallopt mallopt -#define dlmalloc_trim malloc_trim -#define dlmalloc_stats malloc_stats -#define dlmalloc_usable_size malloc_usable_size -#define dlmalloc_footprint malloc_footprint -#define dlmalloc_max_footprint malloc_max_footprint -#define dlmalloc_footprint_limit malloc_footprint_limit -#define dlmalloc_set_footprint_limit malloc_set_footprint_limit -#define dlmalloc_inspect_all malloc_inspect_all -#define dlindependent_calloc independent_calloc -#define dlindependent_comalloc independent_comalloc -#define dlbulk_free bulk_free -#endif /* USE_DL_PREFIX */ - -#if !NO_MALLINFO -#ifndef HAVE_USR_INCLUDE_MALLOC_H -#ifndef _MALLOC_H -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef STRUCT_MALLINFO_DECLARED -#define STRUCT_MALLINFO_DECLARED 1 -struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ -}; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* _MALLOC_H */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* !NO_MALLINFO */ - -/* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. -*/ -void* dlmalloc(size_t); - -/* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cuase the current program to abort. -*/ -void dlfree(void*); - -/* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. -*/ -void* dlcalloc(size_t, size_t); - -/* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. -*/ -void* dlrealloc(void*, size_t); - -/* - realloc_in_place(void* p, size_t n) - Resizes the space allocated for p to size n, only if this can be - done without moving p (i.e., only if there is adjacent space - available if n is greater than p's current allocated size, or n is - less than or equal to p's size). This may be used instead of plain - realloc if an alternative allocation strategy is needed upon failure - to expand space; for example, reallocation of a buffer that must be - memory-aligned or cleared. You can use realloc_in_place to trigger - these alternatives only when needed. - - Returns p if successful; otherwise null. -*/ -void* dlrealloc_in_place(void*, size_t); - -/* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. -*/ -void* dlmemalign(size_t, size_t); - -/* - int posix_memalign(void** pp, size_t alignment, size_t n); - Allocates a chunk of n bytes, aligned in accord with the alignment - argument. Differs from memalign only in that it (1) assigns the - allocated memory to *pp rather than returning it, (2) fails and - returns EINVAL if the alignment is not a power of two (3) fails and - returns ENOMEM if memory cannot be allocated. -*/ -int dlposix_memalign(void**, size_t, size_t); - -/* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. -*/ -void* dlvalloc(size_t); - -/* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt: - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) -*/ -int dlmallopt(int, int); - -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) - - -/* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. -*/ -size_t dlmalloc_footprint(void); - -/* - malloc_max_footprint(); - Returns the maximum number of bytes obtained from the system. This - value will be greater than current footprint if deallocated space - has been reclaimed by the system. The peak number of bytes allocated - by malloc, realloc etc., is less than this value. Unlike mallinfo, - this function returns only a precomputed result, so can be called - frequently to monitor memory consumption. Even if locks are - otherwise defined, this function does not use them, so results might - not be up to date. -*/ -size_t dlmalloc_max_footprint(void); - -/* - malloc_footprint_limit(); - Returns the number of bytes that the heap is allowed to obtain from - the system, returning the last value returned by - malloc_set_footprint_limit, or the maximum size_t value if - never set. The returned value reflects a permission. There is no - guarantee that this number of bytes can actually be obtained from - the system. -*/ -size_t dlmalloc_footprint_limit(void); - -/* - malloc_set_footprint_limit(); - Sets the maximum number of bytes to obtain from the system, causing - failure returns from malloc and related functions upon attempts to - exceed this value. The argument value may be subject to page - rounding to an enforceable limit; this actual value is returned. - Using an argument of the maximum possible size_t effectively - disables checks. If the argument is less than or equal to the - current malloc_footprint, then all future allocations that require - additional system memory will fail. However, invocation cannot - retroactively deallocate existing used memory. -*/ -size_t dlmalloc_set_footprint_limit(size_t bytes); - -/* - malloc_inspect_all(void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg); - Traverses the heap and calls the given handler for each managed - region, skipping all bytes that are (or may be) used for bookkeeping - purposes. Traversal does not include include chunks that have been - directly memory mapped. Each reported region begins at the start - address, and continues up to but not including the end address. The - first used_bytes of the region contain allocated data. If - used_bytes is zero, the region is unallocated. The handler is - invoked with the given callback argument. If locks are defined, they - are held during the entire traversal. It is a bad idea to invoke - other malloc functions from within the handler. - - For example, to count the number of in-use chunks with size greater - than 1000, you could write: - static int count = 0; - void count_chunks(void* start, void* end, size_t used, void* arg) { - if (used >= 1000) ++count; - } - then: - malloc_inspect_all(count_chunks, NULL); - - malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. -*/ -void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), - void* arg); - -#if !NO_MALLINFO -/* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. -*/ - -struct mallinfo dlmallinfo(void); -#endif /* NO_MALLINFO */ - -/* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be freed when it is no longer needed. This can be - done all at once using bulk_free. - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } -*/ -void** dlindependent_calloc(size_t, size_t, void**); - -/* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be freed when it is no longer needed. This can be - done all at once using bulk_free. - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. -*/ -void** dlindependent_comalloc(size_t, size_t*, void**); - -/* - bulk_free(void* array[], size_t n_elements) - Frees and clears (sets to null) each non-null pointer in the given - array. This is likely to be faster than freeing them one-by-one. - If footers are used, pointers that have been allocated in different - mspaces are not freed or cleared, and the count of all such pointers - is returned. For large arrays of pointers with poor locality, it - may be worthwhile to sort this array before calling bulk_free. -*/ -size_t dlbulk_free(void**, size_t n_elements); - -/* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ -void* dlpvalloc(size_t); - -/* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. -*/ -int dlmalloc_trim(size_t); - -/* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. - - malloc_stats is not compiled if NO_MALLOC_STATS is defined. -*/ -void dlmalloc_stats(void); - -#endif /* !ONLY_MSPACES */ - -/* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); -*/ -size_t dlmalloc_usable_size(const void*); - -#if MSPACES - -/* - mspace is an opaque type representing an independent - region of space that supports mspace_malloc, etc. -*/ -typedef void* mspace; - -/* - create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). -*/ -mspace create_mspace(size_t capacity, int locked); - -/* - destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. -*/ -size_t destroy_mspace(mspace msp); - -/* - create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. -*/ -mspace create_mspace_with_base(void* base, size_t capacity, int locked); - -/* - mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. -*/ -int mspace_track_large_chunks(mspace msp, int enable); - -#if !NO_MALLINFO -/* - mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. -*/ -struct mallinfo mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - -/* - An alias for mallopt. -*/ -int mspace_mallopt(int, int); - -/* - The following operate identically to their malloc counterparts - but operate only for the given mspace argument -*/ -void* mspace_malloc(mspace msp, size_t bytes); -void mspace_free(mspace msp, void* mem); -void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); -void* mspace_realloc(mspace msp, void* mem, size_t newsize); -void* mspace_realloc_in_place(mspace msp, void* mem, size_t newsize); -void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); -void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); -void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); -size_t mspace_bulk_free(mspace msp, void**, size_t n_elements); -size_t mspace_usable_size(const void* mem); -void mspace_malloc_stats(mspace msp); -int mspace_trim(mspace msp, size_t pad); -size_t mspace_footprint(mspace msp); -size_t mspace_max_footprint(mspace msp); -size_t mspace_footprint_limit(mspace msp); -size_t mspace_set_footprint_limit(mspace msp, size_t bytes); -void mspace_inspect_all(mspace msp, - void(*handler)(void *, void *, size_t, void*), - void* arg); -#endif /* MSPACES */ - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - -#endif /* MALLOC_280_H */ diff --git a/src/user/lib/vendor/getopt/getopt.c b/src/user/lib/vendor/getopt/getopt.c deleted file mode 100644 index 63c9b51..0000000 --- a/src/user/lib/vendor/getopt/getopt.c +++ /dev/null @@ -1,75 +0,0 @@ -/* Adapted from https://github.com/skeeto/getopt by Chris Wellons */ - -/* A minimal POSIX getopt() implementation in ANSI C - * - * This is free and unencumbered software released into the public domain. - * - * This implementation supports the convention of resetting the option - * parser by assigning optind to 0. This resets the internal state - * appropriately. - * - * Ref: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html - */ -#include <ctype.h> -#include <stdio.h> -#include <string.h> -#include "getopt.h" - -int optind; -int opterr = 1; -int optopt; -char *optarg; - -int -getopt(int argc, char * const argv[], const char *optstring) -{ - static int optpos = 1; - const char *arg; - - /* Reset? */ - if (optind == 0) { - optind = !!argc; - optpos = 1; - } - - arg = argv[optind]; - if (arg && strcmp(arg, "--") == 0) { - optind++; - return -1; - } else if (!arg || arg[0] != '-' || !isalnum(arg[1])) { - return -1; - } else { - const char *opt = strchr(optstring, arg[optpos]); - optopt = arg[optpos]; - if (!opt) { - if (opterr && *optstring != ':') - fprintf(stderr, "%s: illegal option: %c\n", argv[0], optopt); - return '?'; - } else if (opt[1] == ':') { - if (arg[optpos + 1]) { - optarg = (char *)arg + optpos + 1; - optind++; - optpos = 1; - return optopt; - } else if (argv[optind + 1]) { - optarg = (char *)argv[optind + 1]; - optind += 2; - optpos = 1; - return optopt; - } else { - if (opterr && *optstring != ':') - fprintf(stderr, - "%s: option requires an argument: %c\n", - argv[0], optopt); - return *optstring == ':' ? ':' : '?'; - } - } else { - if (!arg[++optpos]) { - optind++; - optpos = 1; - } - return optopt; - } - } -} - diff --git a/src/user/lib/vendor/getopt/getopt.h b/src/user/lib/vendor/getopt/getopt.h deleted file mode 100644 index 4cb075c..0000000 --- a/src/user/lib/vendor/getopt/getopt.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Adapted from https://github.com/skeeto/getopt by Chris Wellons */ - -/* A minimal POSIX getopt() implementation in ANSI C - * - * This is free and unencumbered software released into the public domain. - * - * This implementation supports the convention of resetting the option - * parser by assigning optind to 0. This resets the internal state - * appropriately. - * - * Ref: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html - */ -#ifndef GETOPT_H -#define GETOPT_H - -extern int optind, opterr, optopt; -extern char *optarg; - -int getopt(int argc, char * const argv[], const char *optstring); - -#endif diff --git a/src/user/linker.ld b/src/user/linker.ld deleted file mode 100644 index 4925448..0000000 --- a/src/user/linker.ld +++ /dev/null @@ -1,23 +0,0 @@ -ENTRY(_start) - -SECTIONS -{ - __executable_start = .; - .text BLOCK(4K) : ALIGN(4K) - { - *(.text) - } - .rodata BLOCK(4K) : ALIGN(4K) - { - *(.rodata) - } - .data BLOCK(4K) : ALIGN(4K) - { - *(.data) - } - .bss BLOCK(4K) : ALIGN(4K) - { - *(COMMON) - *(.bss) - } -} |