From 07513581d5a5ef1dafa71eb9513f7ab0e42e2e32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Lanzend=C3=B6rfer?= <leviathan@libresilicon.com>
Date: Fri, 28 Jun 2024 14:46:33 +0100
Subject: [PATCH] Adding functions as separate files

---
 firmware/atoi.c           |   21 +
 firmware/firmware.c       |   37 +-
 firmware/include/stdlib.h |    7 +-
 firmware/include/string.h |   60 +-
 firmware/isdigit.c        |   12 +
 firmware/itoa.c           |   57 ++
 firmware/malloc.c         | 1923 +++++++++++++++++++++++++++++++++++++
 firmware/memchr.c         |   20 +
 firmware/memcmp.c         |   23 +
 firmware/memcpy.c         |   19 +
 firmware/memmove.c        |   22 +
 firmware/memset.c         |   18 +
 firmware/strcasecmp.c     |   24 +
 firmware/strchr.c         |   20 +
 firmware/strcmp.c         |   18 +
 firmware/strcpy.c         |   17 +
 firmware/strdup.c         |   17 +
 firmware/strerror.c       |   21 +
 firmware/string.c         |  123 ---
 firmware/strlen.c         |   17 +
 firmware/strncmp.c        |   19 +
 firmware/strncpy.c        |   16 +
 firmware/strnlen.c        |   17 +
 firmware/strrchr.c        |   19 +
 firmware/strstr.c         |   24 +
 firmware/strtol.c         |  142 +++
 26 files changed, 2549 insertions(+), 164 deletions(-)
 create mode 100644 firmware/atoi.c
 create mode 100644 firmware/isdigit.c
 create mode 100644 firmware/itoa.c
 create mode 100644 firmware/malloc.c
 create mode 100644 firmware/memchr.c
 create mode 100644 firmware/memcmp.c
 create mode 100644 firmware/memcpy.c
 create mode 100644 firmware/memmove.c
 create mode 100644 firmware/memset.c
 create mode 100644 firmware/strcasecmp.c
 create mode 100644 firmware/strchr.c
 create mode 100644 firmware/strcmp.c
 create mode 100644 firmware/strcpy.c
 create mode 100644 firmware/strdup.c
 create mode 100644 firmware/strerror.c
 delete mode 100644 firmware/string.c
 create mode 100644 firmware/strlen.c
 create mode 100644 firmware/strncmp.c
 create mode 100644 firmware/strncpy.c
 create mode 100644 firmware/strnlen.c
 create mode 100644 firmware/strrchr.c
 create mode 100644 firmware/strstr.c
 create mode 100644 firmware/strtol.c

diff --git a/firmware/atoi.c b/firmware/atoi.c
new file mode 100644
index 0000000..0939efa
--- /dev/null
+++ b/firmware/atoi.c
@@ -0,0 +1,21 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <ctype.h>
+#include <stdlib.h>
+
+int atoi(const char *str)
+{
+    int acc = 0;
+
+    for(; str && isdigit(*str); ++str) {
+        acc *= 10;
+        acc += *str - 0x30;
+    }
+
+    return acc;
+}
+
diff --git a/firmware/firmware.c b/firmware/firmware.c
index cccdc66..aaf0253 100644
--- a/firmware/firmware.c
+++ b/firmware/firmware.c
@@ -17,13 +17,18 @@
  *
  */
 
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "rnn.h"
 #include "io.h"
-#include "string.h"
 
 #define MEM_TOTAL 0x20000
 #define MSGBUF 40
-#define MAX_NUM_TOKENS 20
+#define MAX_NUM_TOKENS 40
+
+#define LRFACTOR 2000000000
 
 // a pointer to this is a null pointer, but the compiler does not
 // know that because "sram" is a linker symbol from sections.lds.
@@ -71,6 +76,7 @@ void main()
       // Training the network
       TRAIN,
       TRAIN_STORE_TOKENS,
+      TRAIN_STORE_LEARNING_RATE,
       TRAIN_PROCESS
     } command_mode;
     command_mode = START;
@@ -179,20 +185,20 @@ void main()
                 }
                 else if(!strcmp(msg,"BIAS")) {
                     weight_transferred = get_layer_weight(current_layer, LAYER_VALUE_TYPE_BIAS, neuron_idx, 0);
-                    response = citoa(weight_transferred, weight_string,10);
+                    response = itoa(weight_transferred, numstr, 10);
                 }
                 else {
-                    neuron_idx = atoi(numstr,10);
+                    neuron_idx = atoi(numstr);
                     response = (neuron_idx<current_num_neurons)?msg:"END";
                 }
                 break;
 
             case READ_LAYER_WEIGHTS:
-                weight_idx = atoi(numstr,10);
+                weight_idx = atoi(numstr);
                 response = "END";
                 if(weight_idx<current_max_num_values) {
                     weight_transferred = get_layer_weight(current_layer, current_layer_value, neuron_idx, weight_idx);
-                    response = citoa(weight_transferred, weight_string,10);
+                    response = itoa(weight_transferred, numstr, 10);
                 }
                 break;
 
@@ -234,7 +240,7 @@ void main()
                     value_write_counter = 0;
                 }
                 else {
-                    neuron_idx = atoi(numstr,10);
+                    neuron_idx = atoi(numstr);
                     response = (neuron_idx<current_num_neurons)?msg:"END";
                     value_write_counter = 0;
                 }
@@ -244,7 +250,7 @@ void main()
                 response = "END";
                 if(value_write_counter<current_max_num_values) {
                     response = numstr;
-                    new_value = atoi(numstr,10);
+                    new_value = atoi(numstr);
                     set_layer_weight(current_layer, current_layer_value, neuron_idx, value_write_counter, new_value);
                     value_write_counter++;
                 }
@@ -256,15 +262,19 @@ void main()
                     response = "OK";
                     token_counter = 0;
                 }
-                else if(!strcmp(msg,"RUN")) {
-                    command_mode = TRAIN_PROCESS;
+                else if(!strcmp(msg,"LEARNING_RATE")) {
                     response = "OK";
+                    command_mode = TRAIN_STORE_LEARNING_RATE;
                 }
+                /*else if(!strcmp(msg,"RUN")) {
+                    command_mode = TRAIN_PROCESS;
+                    response = "OK";
+                }*/
                 break;
 
             case TRAIN_STORE_TOKENS:
                 if(token_counter<MAX_NUM_TOKENS) {
-                    new_token = atoi(numstr,10);
+                    new_token = atoi(numstr);
                     token_series[token_counter] = new_token;
                     token_counter++;
                     response = "OK";
@@ -273,6 +283,11 @@ void main()
                 }
                 break;
 
+            case TRAIN_STORE_LEARNING_RATE:
+                new_token = atoi(numstr);
+                response = "OK";
+                break;
+
         }
         write_response(response);
     }
diff --git a/firmware/include/stdlib.h b/firmware/include/stdlib.h
index f116c4c..ea40284 100644
--- a/firmware/include/stdlib.h
+++ b/firmware/include/stdlib.h
@@ -7,17 +7,20 @@
 #ifndef MINLIBC_STDLIB_H
 #define MINLIBC_STDLIB_H
 
-#define NULL            0
+//#define NULL            0
 #define EXIT_SUCCESS    0
 #define EXIT_FAILURE    1
 
-typedef unsigned long int size_t;
+//typedef unsigned long int size_t;
+#include <stddef.h>
 
 double    atof(const char *nptr);
 int       atoi(const char *nptr);
 long int  strtol(const char *nptr, char **endptr, int base);
 unsigned long long strtoull (const char *__restrict, char **__restrict, int);
 
+char *  itoa ( int value, char * str, int base );
+
 char     *getenv(const char *name);
 void      abort(void) __attribute__((noreturn));
 void      exit(int status) __attribute__((noreturn));
diff --git a/firmware/include/string.h b/firmware/include/string.h
index 6f96141..debde27 100644
--- a/firmware/include/string.h
+++ b/firmware/include/string.h
@@ -1,28 +1,32 @@
-#include <stdint.h>
-#include <stdbool.h>
-
-/*
- * Implementation of citoa()
- * 
- * Converts a string into a signed integer
- */
-int atoi(char* str, int base);
-
-/*
- * Converts an integer into a string
- */
-char* citoa(int num, char* str, int base);
-
-/*
- * Compare strings
- * 0: if strings are equal
- * 0: if the first non-matching character in str1 is greater (in ASCII) than that of str2.
- * 0: if the first non-matching character in str1 is lower (in ASCII) than that of str2.
- */
-int strcmp(const char* s1, const char* s2);
-
-/*
- * A small implementation of strcpy
- */
-
-void strcpy(char *s2, char *s1);
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#ifndef MINLIBC_STRING_H
+#define MINLIBC_STRING_H
+
+//#include <sys/types.h>
+#include <stddef.h>
+
+
+size_t  strlen(const char *s);
+size_t  strnlen(const char *s, size_t maxlen);
+char   *strcpy(char *dest, const char *src);
+char   *strncpy(char *dest, const char *src, size_t n);
+int     strcmp(const char *s1, const char *s2);
+char   *strdup(const char *s);
+int     strncmp(const char *s1, const char *s2, size_t n);
+char   *strrchr(const char *s, int c);
+char   *strerror(int errnum);
+char   *strstr(const char *, const char *);
+char   *strchr(const char *, int c);
+
+void   *memcpy(void *dest, const void *src, size_t n);
+void   *memset(void *s, int c, size_t n);
+void   *memmove(void *dest, const void *src, size_t n);
+int     memcmp(const void *s1, const void *s2, size_t n);
+void   *memchr(const void *s, int c, size_t n);
+
+#endif
diff --git a/firmware/isdigit.c b/firmware/isdigit.c
new file mode 100644
index 0000000..1df328f
--- /dev/null
+++ b/firmware/isdigit.c
@@ -0,0 +1,12 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <ctype.h>
+
+int isdigit(int c)
+{
+  return ((c >= '0') && (c <= '9'));
+}
diff --git a/firmware/itoa.c b/firmware/itoa.c
new file mode 100644
index 0000000..2334afb
--- /dev/null
+++ b/firmware/itoa.c
@@ -0,0 +1,57 @@
+// C program to implement itoa()
+#include <stdbool.h>
+#include <stdio.h>
+ 
+// A utility function to reverse a string
+void reverse(char str[], int length)
+{
+    int start = 0;
+    int end = length - 1;
+    while (start < end) {
+        char temp = str[start];
+        str[start] = str[end];
+        str[end] = temp;
+        end--;
+        start++;
+    }
+}
+
+char *  itoa ( int num, char * str, int base )
+{
+    int i = 0;
+    bool isNegative = false;
+ 
+    /* Handle 0 explicitly, otherwise empty string is
+     * printed for 0 */
+    if (num == 0) {
+        str[i++] = '0';
+        str[i] = '\0';
+        return str;
+    }
+ 
+    // In standard itoa(), negative numbers are handled
+    // only with base 10. Otherwise numbers are
+    // considered unsigned.
+    if (num < 0 && base == 10) {
+        isNegative = true;
+        num = -num;
+    }
+ 
+    // Process individual digits
+    while (num != 0) {
+        int rem = num % base;
+        str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
+        num = num / base;
+    }
+ 
+    // If number is negative, append '-'
+    if (isNegative)
+        str[i++] = '-';
+ 
+    str[i] = '\0'; // Append string terminator
+ 
+    // Reverse the string
+    reverse(str, i);
+ 
+    return str;
+}
diff --git a/firmware/malloc.c b/firmware/malloc.c
new file mode 100644
index 0000000..9d03735
--- /dev/null
+++ b/firmware/malloc.c
@@ -0,0 +1,1923 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+/*	$OpenBSD: malloc.c,v 1.83 2006/05/14 19:53:40 otto Exp $	*/
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * Defining MALLOC_EXTRA_SANITY will enable extra checks which are
+ * related to internal conditions and consistency in malloc.c. This has
+ * a noticeable runtime performance hit, and generally will not do you
+ * any good unless you fiddle with the internals of malloc or want
+ * to catch random pointer corruption as early as possible.
+ */
+#ifndef	MALLOC_EXTRA_SANITY
+#undef	MALLOC_EXTRA_SANITY
+#endif
+
+/*
+ * Defining MALLOC_STATS will enable you to call malloc_dump() and set
+ * the [dD] options in the MALLOC_OPTIONS environment variable.
+ * It has no run-time performance hit, but does pull in stdio...
+ */
+#ifndef	MALLOC_STATS
+#undef	MALLOC_STATS
+#endif
+
+/*
+ * What to use for Junk.  This is the byte value we use to fill with
+ * when the 'J' option is enabled.
+ */
+#define SOME_JUNK	0xd0	/* as in "Duh" :-) */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <runtime_reqs.h>
+
+typedef unsigned char  u_char;
+typedef unsigned short u_short;
+typedef unsigned long  u_long;
+typedef char          *caddr_t;
+
+// These are standard for x86/Xen
+#define PGSHIFT 12
+#define STDERR_FILENO 3
+#define arc4random() 2357
+
+struct iovec {
+  char *iov_base;
+  size_t iov_len;
+};
+
+// This is used for output by this module
+static void writev(int d __attribute__((unused)), 
+                   const struct iovec *iov, int iovcnt)
+{
+  int i;
+
+  for(i = 0; i < iovcnt; i++)
+    runtime_write(iov[i].iov_len, iov[i].iov_base); 
+}
+
+static void warnx(const char *fmt, ...)
+{
+  va_list args;
+
+  va_start(args, fmt);
+  printf("MALLOC WARNING: ");
+  vfprintf(0, fmt, args);
+  va_end(args);
+}
+
+static unsigned long mallock;
+
+// No locking for now
+#define _MALLOC_LOCK_INIT() { mallock = 0; }/* */
+#define _MALLOC_LOCK() while(!__sync_bool_compare_and_swap(&mallock, 0, 1)) {}
+#define _MALLOC_UNLOCK() mallock = 0;
+
+/*
+ * The basic parameters you can tweak.
+ *
+ * malloc_pageshift	pagesize = 1 << malloc_pageshift
+ *			It's probably best if this is the native
+ *			page size, but it shouldn't have to be.
+ *
+ * malloc_minsize	minimum size of an allocation in bytes.
+ *			If this is too small it's too much work
+ *			to manage them.  This is also the smallest
+ *			unit of alignment used for the storage
+ *			returned by malloc/realloc.
+ *
+ */
+
+#if defined(__sparc__)
+#define	malloc_pageshift	13U
+#endif /* __sparc__ */
+
+#ifndef malloc_pageshift
+#define malloc_pageshift	(PGSHIFT)
+#endif
+
+/*
+ * No user serviceable parts behind this point.
+ *
+ * This structure describes a page worth of chunks.
+ */
+struct pginfo {
+	struct pginfo	*next;	/* next on the free list */
+	void		*page;	/* Pointer to the page */
+	u_short		size;	/* size of this page's chunks */
+	u_short		shift;	/* How far to shift for this size chunks */
+	u_short		free;	/* How many free chunks */
+	u_short		total;	/* How many chunk */
+	u_long		bits[1];/* Which chunks are free */
+};
+
+static __inline__ void free_pages(void *, u_long, struct pginfo *);
+static __inline__ void free_bytes(void *, u_long, struct pginfo *);
+
+/* How many bits per u_long in the bitmap */
+#define	MALLOC_BITS	(8 * sizeof(u_long))
+
+/*
+ * This structure describes a number of free pages.
+ */
+struct pgfree {
+	struct pgfree	*next;	/* next run of free pages */
+	struct pgfree	*prev;	/* prev run of free pages */
+	void		*page;	/* pointer to free pages */
+	void		*pdir;	/* pointer to the base page's dir */
+	size_t		size;	/* number of bytes free */
+};
+
+/*
+ * Magic values to put in the page_directory
+ */
+#define MALLOC_NOT_MINE	((struct pginfo*) 0)
+#define MALLOC_FREE	((struct pginfo*) 1)
+#define MALLOC_FIRST	((struct pginfo*) 2)
+#define MALLOC_FOLLOW	((struct pginfo*) 3)
+#define MALLOC_MAGIC	((struct pginfo*) 4)
+
+#ifndef malloc_minsize
+#define malloc_minsize			16UL
+#endif
+
+#if !defined(malloc_pagesize)
+#define malloc_pagesize			(1UL<<malloc_pageshift)
+#endif
+
+#if ((1UL<<malloc_pageshift) != malloc_pagesize)
+#error	"(1UL<<malloc_pageshift) != malloc_pagesize"
+#endif
+
+#ifndef malloc_maxsize
+#define malloc_maxsize			((malloc_pagesize)>>1)
+#endif
+
+/* A mask for the offset inside a page. */
+#define malloc_pagemask	((malloc_pagesize)-1)
+
+#define	pageround(foo)	(((foo) + (malloc_pagemask)) & ~malloc_pagemask)
+#define	ptr2index(foo)	(((u_long)(foo) >> malloc_pageshift)+malloc_pageshift)
+#define	index2ptr(idx)	((void*)(((idx)-malloc_pageshift)<<malloc_pageshift))
+
+/* Set when initialization has been done */
+static unsigned int	malloc_started;
+
+/* Number of free pages we cache */
+static unsigned int	malloc_cache = 16;
+
+/* Structure used for linking discrete directory pages. */
+struct pdinfo {
+	struct pginfo	**base;
+	struct pdinfo	*prev;
+	struct pdinfo	*next;
+	u_long		dirnum;
+};
+static struct pdinfo *last_dir;	/* Caches to the last and previous */
+static struct pdinfo *prev_dir;	/* referenced directory pages. */
+
+static int pdir_lookup(u_long index, struct pdinfo ** pdi);
+
+static size_t	pdi_off;
+static u_long	pdi_mod;
+#define	PD_IDX(num)	((num) / (malloc_pagesize/sizeof(struct pginfo *)))
+#define	PD_OFF(num)	((num) & ((malloc_pagesize/sizeof(struct pginfo *))-1))
+#define	PI_IDX(index)	((index) / pdi_mod)
+#define	PI_OFF(index)	((index) % pdi_mod)
+
+/* The last index in the page directory we care about */
+static u_long	last_index;
+
+/* Pointer to page directory. Allocated "as if with" malloc */
+static struct pginfo **page_dir;
+
+/* Free pages line up here */
+static struct pgfree free_list;
+
+/* Abort(), user doesn't handle problems. */
+static int	malloc_abort = 2;
+
+/* Are we trying to die ? */
+static int	suicide;
+
+#ifdef MALLOC_STATS
+/* dump statistics */
+static int	malloc_stats;
+#endif
+
+/* avoid outputting warnings? */
+static int	malloc_silent;
+
+/* always realloc ? */
+static int	malloc_realloc;
+
+/* mprotect free pages PROT_NONE? */
+static int	malloc_freeprot;
+
+/* use guard pages after allocations? */
+static size_t	malloc_guard = 0;
+static size_t	malloc_guarded;
+/* align pointers to end of page? */
+static int	malloc_ptrguard;
+
+static int	malloc_hint;
+
+/* xmalloc behaviour ? */
+static int	malloc_xmalloc;
+
+/* zero fill ? */
+static int	malloc_zero;
+
+/* junk fill ? */
+static int	malloc_junk;
+
+#ifdef __FreeBSD__
+/* utrace ? */
+static int	malloc_utrace;
+
+struct ut {
+	void		*p;
+	size_t		s;
+	void		*r;
+};
+
+void		utrace(struct ut *, int);
+
+#define UTRACE(a, b, c) \
+	if (malloc_utrace) \
+		{struct ut u; u.p=a; u.s = b; u.r=c; utrace(&u, sizeof u);}
+#else				/* !__FreeBSD__ */
+#define UTRACE(a,b,c)
+#endif
+
+/* Status of malloc. */
+static int	malloc_active;
+
+/* Allocated memory. */
+static size_t	malloc_used;
+
+/* My last break. */
+static caddr_t	malloc_brk;
+
+/* One location cache for free-list holders. */
+static struct pgfree *px;
+
+/* Compile-time options. */
+char		*malloc_options;
+
+/* Name of the current public function. */
+static char	*malloc_func;
+
+#define MMAP(size) \
+	mmap((void *)0, (size), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, \
+	    -1, (off_t)0)
+
+/*
+ * Necessary function declarations.
+ */
+static void	*imalloc(size_t size);
+static void	ifree(void *ptr);
+static void	*irealloc(void *ptr, size_t size);
+static void	*malloc_bytes(size_t size);
+
+/*
+ * Function for page directory lookup.
+ */
+static int
+pdir_lookup(u_long index, struct pdinfo ** pdi)
+{
+	struct pdinfo	*spi;
+	u_long		pidx = PI_IDX(index);
+
+	if (last_dir != NULL && PD_IDX(last_dir->dirnum) == pidx)
+		*pdi = last_dir;
+	else if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) == pidx)
+		*pdi = prev_dir;
+	else if (last_dir != NULL && prev_dir != NULL) {
+		if ((PD_IDX(last_dir->dirnum) > pidx) ?
+		    (PD_IDX(last_dir->dirnum) - pidx) :
+		    (pidx - PD_IDX(last_dir->dirnum))
+		    < (PD_IDX(prev_dir->dirnum) > pidx) ?
+		    (PD_IDX(prev_dir->dirnum) - pidx) :
+		    (pidx - PD_IDX(prev_dir->dirnum)))
+			*pdi = last_dir;
+		else
+			*pdi = prev_dir;
+
+		if (PD_IDX((*pdi)->dirnum) > pidx) {
+			for (spi = (*pdi)->prev;
+			    spi != NULL && PD_IDX(spi->dirnum) > pidx;
+			    spi = spi->prev)
+				*pdi = spi;
+			if (spi != NULL)
+				*pdi = spi;
+		} else
+			for (spi = (*pdi)->next;
+			    spi != NULL && PD_IDX(spi->dirnum) <= pidx;
+			    spi = spi->next)
+				*pdi = spi;
+	} else {
+		*pdi = (struct pdinfo *) ((caddr_t) page_dir + pdi_off);
+		for (spi = *pdi;
+		    spi != NULL && PD_IDX(spi->dirnum) <= pidx;
+		    spi = spi->next)
+			*pdi = spi;
+	}
+
+	return ((PD_IDX((*pdi)->dirnum) == pidx) ? 0 :
+	    (PD_IDX((*pdi)->dirnum) > pidx) ? 1 : -1);
+}
+
+#ifdef MALLOC_STATS
+void
+malloc_dump(int fd)
+{
+	char		buf[1024];
+	struct pginfo	**pd;
+	struct pgfree	*pf;
+	struct pdinfo	*pi;
+	u_long		j;
+
+	pd = page_dir;
+	pi = (struct pdinfo *) ((caddr_t) pd + pdi_off);
+
+	/* print out all the pages */
+	for (j = 0; j <= last_index;) {
+		snprintf(buf, sizeof buf, "%08lx %5lu ", j << malloc_pageshift, j);
+		write(fd, buf, strlen(buf));
+		if (pd[PI_OFF(j)] == MALLOC_NOT_MINE) {
+			for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_NOT_MINE;) {
+				if (!PI_OFF(++j)) {
+					if ((pi = pi->next) == NULL ||
+					    PD_IDX(pi->dirnum) != PI_IDX(j))
+						break;
+					pd = pi->base;
+					j += pdi_mod;
+				}
+			}
+			j--;
+			snprintf(buf, sizeof buf, ".. %5lu not mine\n", j);
+			write(fd, buf, strlen(buf));
+		} else if (pd[PI_OFF(j)] == MALLOC_FREE) {
+			for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_FREE;) {
+				if (!PI_OFF(++j)) {
+					if ((pi = pi->next) == NULL ||
+					    PD_IDX(pi->dirnum) != PI_IDX(j))
+						break;
+					pd = pi->base;
+					j += pdi_mod;
+				}
+			}
+			j--;
+			snprintf(buf, sizeof buf, ".. %5lu free\n", j);
+			write(fd, buf, strlen(buf));
+		} else if (pd[PI_OFF(j)] == MALLOC_FIRST) {
+			for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_FOLLOW;) {
+				if (!PI_OFF(++j)) {
+					if ((pi = pi->next) == NULL ||
+					    PD_IDX(pi->dirnum) != PI_IDX(j))
+						break;
+					pd = pi->base;
+					j += pdi_mod;
+				}
+			}
+			j--;
+			snprintf(buf, sizeof buf, ".. %5lu in use\n", j);
+			write(fd, buf, strlen(buf));
+		} else if (pd[PI_OFF(j)] < MALLOC_MAGIC) {
+			snprintf(buf, sizeof buf, "(%p)\n", pd[PI_OFF(j)]);
+			write(fd, buf, strlen(buf));
+		} else {
+			snprintf(buf, sizeof buf, "%p %d (of %d) x %d @ %p --> %p\n",
+			    pd[PI_OFF(j)], pd[PI_OFF(j)]->free,
+			    pd[PI_OFF(j)]->total, pd[PI_OFF(j)]->size,
+			    pd[PI_OFF(j)]->page, pd[PI_OFF(j)]->next);
+			write(fd, buf, strlen(buf));
+		}
+		if (!PI_OFF(++j)) {
+			if ((pi = pi->next) == NULL)
+				break;
+			pd = pi->base;
+			j += (1 + PD_IDX(pi->dirnum) - PI_IDX(j)) * pdi_mod;
+		}
+	}
+
+	for (pf = free_list.next; pf; pf = pf->next) {
+		snprintf(buf, sizeof buf, "Free: @%p [%p...%p[ %ld ->%p <-%p\n",
+		    pf, pf->page, (char *)pf->page + pf->size,
+		    pf->size, pf->prev, pf->next);
+		write(fd, buf, strlen(buf));
+		if (pf == pf->next) {
+			snprintf(buf, sizeof buf, "Free_list loops\n");
+			write(fd, buf, strlen(buf));
+			break;
+		}
+	}
+
+	/* print out various info */
+	snprintf(buf, sizeof buf, "Minsize\t%lu\n", malloc_minsize);
+	write(fd, buf, strlen(buf));
+	snprintf(buf, sizeof buf, "Maxsize\t%lu\n", malloc_maxsize);
+	write(fd, buf, strlen(buf));
+	snprintf(buf, sizeof buf, "Pagesize\t%lu\n", malloc_pagesize);
+	write(fd, buf, strlen(buf));
+	snprintf(buf, sizeof buf, "Pageshift\t%u\n", malloc_pageshift);
+	write(fd, buf, strlen(buf));
+	snprintf(buf, sizeof buf, "In use\t%lu\n", (u_long) malloc_used);
+	write(fd, buf, strlen(buf));
+	snprintf(buf, sizeof buf, "Guarded\t%lu\n", (u_long) malloc_guarded);
+	write(fd, buf, strlen(buf));
+}
+#endif /* MALLOC_STATS */
+
+char	*__progname = "HALVM";
+
+static void
+wrterror(char *p)
+{
+	char		*q = " error: ";
+	struct iovec	iov[5];
+
+	iov[0].iov_base = __progname;
+	iov[0].iov_len = strlen(__progname);
+	iov[1].iov_base = malloc_func;
+	iov[1].iov_len = strlen(malloc_func);
+	iov[2].iov_base = q;
+	iov[2].iov_len = strlen(q);
+	iov[3].iov_base = p;
+	iov[3].iov_len = strlen(p);
+	iov[4].iov_base = "\n";
+	iov[4].iov_len = 1;
+	writev(STDERR_FILENO, iov, 5);
+
+	suicide = 1;
+#ifdef MALLOC_STATS
+	if (malloc_stats)
+		malloc_dump(STDERR_FILENO);
+#endif /* MALLOC_STATS */
+	malloc_active--;
+	if (malloc_abort)
+		abort();
+}
+
+static void
+wrtwarning(char *p)
+{
+	char		*q = " warning: ";
+	struct iovec	iov[5];
+
+	if (malloc_abort)
+		wrterror(p);
+	else if (malloc_silent)
+		return;
+
+	iov[0].iov_base = __progname;
+	iov[0].iov_len = strlen(__progname);
+	iov[1].iov_base = malloc_func;
+	iov[1].iov_len = strlen(malloc_func);
+	iov[2].iov_base = q;
+	iov[2].iov_len = strlen(q);
+	iov[3].iov_base = p;
+	iov[3].iov_len = strlen(p);
+	iov[4].iov_base = "\n";
+	iov[4].iov_len = 1;
+	
+	writev(STDERR_FILENO, iov, 5);
+}
+
+#ifdef MALLOC_STATS
+static void
+malloc_exit(void)
+{
+	char	*q = "malloc() warning: Couldn't dump stats\n";
+	int	save_errno = errno, fd;
+
+	fd = open("malloc.out", O_RDWR|O_APPEND);
+	if (fd != -1) {
+		malloc_dump(fd);
+		close(fd);
+	} else
+		write(STDERR_FILENO, q, strlen(q));
+	errno = save_errno;
+}
+#endif /* MALLOC_STATS */
+
+/*
+ * Allocate a number of pages from the OS
+ */
+static void *
+map_pages(size_t pages)
+{
+	struct pdinfo	*pi, *spi;
+	struct pginfo	**pd;
+	u_long		idx, pidx, lidx;
+	caddr_t		result, tail;
+	u_long		index, lindex;
+	void 		*pdregion = NULL;
+	size_t		dirs, cnt;
+
+	pages <<= malloc_pageshift;
+	result = MMAP(pages + malloc_guard);
+	if (result == MAP_FAILED) {
+#ifdef MALLOC_EXTRA_SANITY
+		wrtwarning("(ES): map_pages fails");
+#endif /* MALLOC_EXTRA_SANITY */
+		errno = ENOMEM;
+		return (NULL);
+	}
+	index = ptr2index(result);
+	tail = result + pages + malloc_guard;
+	lindex = ptr2index(tail) - 1;
+	if (malloc_guard)
+		mprotect(result + pages, malloc_guard, PROT_NONE);
+
+	pidx = PI_IDX(index);
+	lidx = PI_IDX(lindex);
+
+	if (tail > malloc_brk) {
+		malloc_brk = tail;
+		last_index = lindex;
+	}
+
+	dirs = lidx - pidx;
+
+	/* Insert directory pages, if needed. */
+	if (pdir_lookup(index, &pi) != 0)
+		dirs++;
+	if (dirs > 0) {
+		pdregion = MMAP(malloc_pagesize * dirs);
+		if (pdregion == MAP_FAILED) {
+			munmap(result, tail - result);
+#ifdef MALLOC_EXTRA_SANITY
+		wrtwarning("(ES): map_pages fails");
+#endif
+			errno = ENOMEM;
+			return (NULL);
+		}
+	}
+	cnt = 0;
+	for (idx = pidx, spi = pi; idx <= lidx; idx++) {
+		if (pi == NULL || PD_IDX(pi->dirnum) != idx) {
+			pd = (struct pginfo **)((char *)pdregion +
+			    cnt * malloc_pagesize);
+			cnt++;
+			memset(pd, 0, malloc_pagesize);
+			pi = (struct pdinfo *) ((caddr_t) pd + pdi_off);
+			pi->base = pd;
+			pi->prev = spi;
+			pi->next = spi->next;
+			pi->dirnum = idx * (malloc_pagesize /
+			    sizeof(struct pginfo *));
+
+			if (spi->next != NULL)
+				spi->next->prev = pi;
+			spi->next = pi;
+		}
+		if (idx > pidx && idx < lidx) {
+			pi->dirnum += pdi_mod;
+		} else if (idx == pidx) {
+			if (pidx == lidx) {
+				pi->dirnum += (u_long)(tail - result) >>
+				    malloc_pageshift;
+			} else {
+				pi->dirnum += pdi_mod - PI_OFF(index);
+			}
+		} else {
+			pi->dirnum += PI_OFF(ptr2index(tail - 1)) + 1;
+		}
+#ifdef MALLOC_EXTRA_SANITY
+		if (PD_OFF(pi->dirnum) > pdi_mod || PD_IDX(pi->dirnum) > idx) {
+			wrterror("(ES): pages directory overflow");
+			errno = EFAULT;
+			return (NULL);
+		}
+#endif /* MALLOC_EXTRA_SANITY */
+		if (idx == pidx && pi != last_dir) {
+			prev_dir = last_dir;
+			last_dir = pi;
+		}
+		spi = pi;
+		pi = spi->next;
+	}
+#ifdef MALLOC_EXTRA_SANITY
+	if (cnt > dirs)
+		wrtwarning("(ES): cnt > dirs");
+#endif /* MALLOC_EXTRA_SANITY */
+	if (cnt < dirs)
+		munmap((char *)pdregion + cnt * malloc_pagesize,
+		    (dirs - cnt) * malloc_pagesize);
+
+	return (result);
+}
+
+/*
+ * Initialize the world
+ */
+static void
+malloc_init(void)
+{
+	char		*p; // , b[64];
+	int		i, j, save_errno = errno;
+
+	_MALLOC_LOCK_INIT();
+
+#ifdef MALLOC_EXTRA_SANITY
+	malloc_junk = 1;
+#endif /* MALLOC_EXTRA_SANITY */
+
+	for (i = 0; i < 3; i++) {
+		switch (i) {
+	/*	case 0:
+			j = readlink("/etc/malloc.conf", b, sizeof b - 1);
+			if (j <= 0)
+				continue;
+			b[j] = '\0';
+			p = b;
+			break;
+		case 1:
+			if (issetugid() == 0)
+				p = getenv("MALLOC_OPTIONS");
+			else
+				continue;
+			break;
+	*/	case 2:
+			p = malloc_options;
+			break;
+		default:
+			p = NULL;
+		}
+
+		for (; p != NULL && *p != '\0'; p++) {
+			switch (*p) {
+			case '>':
+				malloc_cache <<= 1;
+				break;
+			case '<':
+				malloc_cache >>= 1;
+				break;
+			case 'a':
+				malloc_abort = 0;
+				break;
+			case 'A':
+				malloc_abort = 1;
+				break;
+#ifdef MALLOC_STATS
+			case 'd':
+				malloc_stats = 0;
+				break;
+			case 'D':
+				malloc_stats = 1;
+				break;
+#endif /* MALLOC_STATS */
+			case 'f':
+				malloc_freeprot = 0;
+				break;
+			case 'F':
+				malloc_freeprot = 1;
+				break;
+			case 'g':
+				malloc_guard = 0;
+				break;
+			case 'G':
+				malloc_guard = malloc_pagesize;
+				break;
+			case 'h':
+				malloc_hint = 0;
+				break;
+			case 'H':
+				malloc_hint = 1;
+				break;
+			case 'j':
+				malloc_junk = 0;
+				break;
+			case 'J':
+				malloc_junk = 1;
+				break;
+			case 'n':
+				malloc_silent = 0;
+				break;
+			case 'N':
+				malloc_silent = 1;
+				break;
+			case 'p':
+				malloc_ptrguard = 0;
+				break;
+			case 'P':
+				malloc_ptrguard = 1;
+				break;
+			case 'r':
+				malloc_realloc = 0;
+				break;
+			case 'R':
+				malloc_realloc = 1;
+				break;
+#ifdef __FreeBSD__
+			case 'u':
+				malloc_utrace = 0;
+				break;
+			case 'U':
+				malloc_utrace = 1;
+				break;
+#endif /* __FreeBSD__ */
+			case 'x':
+				malloc_xmalloc = 0;
+				break;
+			case 'X':
+				malloc_xmalloc = 1;
+				break;
+			case 'z':
+				malloc_zero = 0;
+				break;
+			case 'Z':
+				malloc_zero = 1;
+				break;
+			default:
+				j = malloc_abort;
+				malloc_abort = 0;
+				wrtwarning("unknown char in MALLOC_OPTIONS");
+				malloc_abort = j;
+				break;
+			}
+		}
+	}
+
+	UTRACE(0, 0, 0);
+
+	/*
+	 * We want junk in the entire allocation, and zero only in the part
+	 * the user asked for.
+	 */
+	if (malloc_zero)
+		malloc_junk = 1;
+
+#ifdef MALLOC_STATS
+	if (malloc_stats && (atexit(malloc_exit) == -1))
+		wrtwarning("atexit(2) failed."
+		    "  Will not be able to dump malloc stats on exit");
+#endif /* MALLOC_STATS */
+
+	/* Allocate one page for the page directory. */
+	page_dir = (struct pginfo **)MMAP(malloc_pagesize);
+
+	if (page_dir == MAP_FAILED) {
+		wrterror("mmap(2) failed, check limits");
+		errno = ENOMEM;
+		return;
+	}
+	pdi_off = (malloc_pagesize - sizeof(struct pdinfo)) & ~(malloc_minsize - 1);
+	pdi_mod = pdi_off / sizeof(struct pginfo *);
+
+	last_dir = (struct pdinfo *) ((caddr_t) page_dir + pdi_off);
+	last_dir->base = page_dir;
+	last_dir->prev = last_dir->next = NULL;
+	last_dir->dirnum = malloc_pageshift;
+
+	/* Been here, done that. */
+	malloc_started++;
+
+	/* Recalculate the cache size in bytes, and make sure it's nonzero. */
+	if (!malloc_cache)
+		malloc_cache++;
+	malloc_cache <<= malloc_pageshift;
+	errno = save_errno;
+}
+
+/*
+ * Allocate a number of complete pages
+ */
+static void *
+malloc_pages(size_t size)
+{
+	void		*p, *delay_free = NULL, *tp;
+	int		i;
+	struct pginfo	**pd;
+	struct pdinfo	*pi;
+	u_long		pidx, index;
+	struct pgfree	*pf;
+
+	size = pageround(size) + malloc_guard;
+
+	p = NULL;
+	/* Look for free pages before asking for more */
+	for (pf = free_list.next; pf; pf = pf->next) {
+
+#ifdef MALLOC_EXTRA_SANITY
+		if (pf->size & malloc_pagemask) {
+			wrterror("(ES): junk length entry on free_list");
+			errno = EFAULT;
+			return (NULL);
+		}
+		if (!pf->size) {
+			wrterror("(ES): zero length entry on free_list");
+			errno = EFAULT;
+			return (NULL);
+		}
+		if (pf->page > (pf->page + pf->size)) {
+			wrterror("(ES): sick entry on free_list");
+			errno = EFAULT;
+			return (NULL);
+		}
+		if ((pi = pf->pdir) == NULL) {
+			wrterror("(ES): invalid page directory on free-list");
+			errno = EFAULT;
+			return (NULL);
+		}
+		if ((pidx = PI_IDX(ptr2index(pf->page))) != PD_IDX(pi->dirnum)) {
+			wrterror("(ES): directory index mismatch on free-list");
+			errno = EFAULT;
+			return (NULL);
+		}
+		pd = pi->base;
+		if (pd[PI_OFF(ptr2index(pf->page))] != MALLOC_FREE) {
+			wrterror("(ES): non-free first page on free-list");
+			errno = EFAULT;
+			return (NULL);
+		}
+		pidx = PI_IDX(ptr2index((pf->page) + (pf->size)) - 1);
+		for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx;
+		    pi = pi->next)
+			;
+		if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+			wrterror("(ES): last page not referenced in page directory");
+			errno = EFAULT;
+			return (NULL);
+		}
+		pd = pi->base;
+		if (pd[PI_OFF(ptr2index((pf->page) + (pf->size)) - 1)] != MALLOC_FREE) {
+			wrterror("(ES): non-free last page on free-list");
+			errno = EFAULT;
+			return (NULL);
+		}
+#endif /* MALLOC_EXTRA_SANITY */
+
+		if (pf->size < size)
+			continue;
+
+		if (pf->size == size) {
+			p = pf->page;
+			pi = pf->pdir;
+			if (pf->next != NULL)
+				pf->next->prev = pf->prev;
+			pf->prev->next = pf->next;
+			delay_free = pf;
+			break;
+		}
+		p = pf->page;
+		pf->page = (char *) pf->page + size;
+		pf->size -= size;
+		pidx = PI_IDX(ptr2index(pf->page));
+		for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx;
+		    pi = pi->next)
+			;
+		if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+			wrterror("(ES): hole in directories");
+			errno = EFAULT;
+			return (NULL);
+		}
+		tp = pf->pdir;
+		pf->pdir = pi;
+		pi = tp;
+		break;
+	}
+
+	size -= malloc_guard;
+
+#ifdef MALLOC_EXTRA_SANITY
+	if (p != NULL && pi != NULL) {
+		pidx = PD_IDX(pi->dirnum);
+		pd = pi->base;
+	}
+	if (p != NULL && pd[PI_OFF(ptr2index(p))] != MALLOC_FREE) {
+		wrterror("(ES): allocated non-free page on free-list");
+		errno = EFAULT;
+		return (NULL);
+	}
+#endif /* MALLOC_EXTRA_SANITY */
+
+	if (p != NULL && (malloc_guard || malloc_freeprot))
+		mprotect(p, size, PROT_READ | PROT_WRITE);
+
+	size >>= malloc_pageshift;
+	/* Map new pages */
+	if (p == NULL)
+		p = map_pages(size);
+	if (p != NULL) {
+		index = ptr2index(p);
+		pidx = PI_IDX(index);
+		pdir_lookup(index, &pi);
+#ifdef MALLOC_EXTRA_SANITY
+		if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+			wrterror("(ES): mapped pages not found in directory");
+			errno = EFAULT;
+			return (NULL);
+		}
+#endif /* MALLOC_EXTRA_SANITY */
+		if (pi != last_dir) {
+			prev_dir = last_dir;
+			last_dir = pi;
+		}
+		pd = pi->base;
+		pd[PI_OFF(index)] = MALLOC_FIRST;
+		for (i = 1; (size_t)i < size; i++) {
+			if (!PI_OFF(index + i)) {
+				pidx++;
+				pi = pi->next;
+#ifdef MALLOC_EXTRA_SANITY
+				if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+					wrterror("(ES): hole in mapped pages directory");
+					errno = EFAULT;
+					return (NULL);
+				}
+#endif /* MALLOC_EXTRA_SANITY */
+				pd = pi->base;
+			}
+			pd[PI_OFF(index + i)] = MALLOC_FOLLOW;
+		}
+		if (malloc_guard) {
+			if (!PI_OFF(index + i)) {
+				pidx++;
+				pi = pi->next;
+#ifdef MALLOC_EXTRA_SANITY
+				if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+					wrterror("(ES): hole in mapped pages directory");
+					errno = EFAULT;
+					return (NULL);
+				}
+#endif /* MALLOC_EXTRA_SANITY */
+				pd = pi->base;
+			}
+			pd[PI_OFF(index + i)] = MALLOC_FIRST;
+		}
+		malloc_used += size << malloc_pageshift;
+		malloc_guarded += malloc_guard;
+
+		if (malloc_junk)
+			memset(p, SOME_JUNK, size << malloc_pageshift);
+	}
+	if (delay_free) {
+		if (px == NULL)
+			px = delay_free;
+		else
+			ifree(delay_free);
+	}
+	return (p);
+}
+
+/*
+ * Allocate a page of fragments
+ */
+
+static __inline__ int
+malloc_make_chunks(int bits)
+{
+	struct pginfo	*bp, **pd;
+	struct pdinfo	*pi;
+#ifdef	MALLOC_EXTRA_SANITY
+	u_long		pidx;
+#endif	/* MALLOC_EXTRA_SANITY */
+	void		*pp;
+	long		i, k;
+	size_t		l;
+
+	/* Allocate a new bucket */
+	pp = malloc_pages((size_t) malloc_pagesize);
+	if (pp == NULL)
+		return (0);
+
+	/* Find length of admin structure */
+	l = sizeof *bp - sizeof(u_long);
+	l += sizeof(u_long) *
+	    (((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS);
+
+	/* Don't waste more than two chunks on this */
+
+	/*
+	 * If we are to allocate a memory protected page for the malloc(0)
+	 * case (when bits=0), it must be from a different page than the
+	 * pginfo page.
+	 * --> Treat it like the big chunk alloc, get a second data page.
+	 */
+	if (bits != 0 && (1UL << (bits)) <= l + l) {
+		bp = (struct pginfo *) pp;
+	} else {
+		bp = (struct pginfo *) imalloc(l);
+		if (bp == NULL) {
+			ifree(pp);
+			return (0);
+		}
+	}
+
+	/* memory protect the page allocated in the malloc(0) case */
+	if (bits == 0) {
+		bp->size = 0;
+		bp->shift = 1;
+		i = malloc_minsize - 1;
+		while (i >>= 1)
+			bp->shift++;
+		bp->total = bp->free = malloc_pagesize >> bp->shift;
+		bp->page = pp;
+
+		k = mprotect(pp, malloc_pagesize, PROT_NONE);
+		if (k < 0) {
+			ifree(pp);
+			ifree(bp);
+			return (0);
+		}
+	} else {
+		bp->size = (1UL << bits);
+		bp->shift = bits;
+		bp->total = bp->free = malloc_pagesize >> bits;
+		bp->page = pp;
+	}
+
+	/* set all valid bits in the bitmap */
+	k = bp->total;
+	i = 0;
+
+	/* Do a bunch at a time */
+	for (; (k - i) >= (int)MALLOC_BITS; i += MALLOC_BITS)
+		bp->bits[i / MALLOC_BITS] = ~0UL;
+
+	for (; i < k; i++)
+		bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS);
+
+	k = (long)l;
+	if (bp == bp->page) {
+		/* Mark the ones we stole for ourselves */
+		for (i = 0; k > 0; i++) {
+			bp->bits[i / MALLOC_BITS] &= ~(1UL << (i % MALLOC_BITS));
+			bp->free--;
+			bp->total--;
+			k -= (1 << bits);
+		}
+	}
+	/* MALLOC_LOCK */
+
+	pdir_lookup(ptr2index(pp), &pi);
+#ifdef MALLOC_EXTRA_SANITY
+	pidx = PI_IDX(ptr2index(pp));
+	if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+		wrterror("(ES): mapped pages not found in directory");
+		errno = EFAULT;
+		return (0);
+	}
+#endif /* MALLOC_EXTRA_SANITY */
+	if (pi != last_dir) {
+		prev_dir = last_dir;
+		last_dir = pi;
+	}
+	pd = pi->base;
+	pd[PI_OFF(ptr2index(pp))] = bp;
+
+	bp->next = page_dir[bits];
+	page_dir[bits] = bp;
+
+	/* MALLOC_UNLOCK */
+	return (1);
+}
+
+/*
+ * Allocate a fragment
+ */
+static void *
+malloc_bytes(size_t size)
+{
+	int		i, j;
+	size_t		k;
+	u_long		u, *lp;
+	struct pginfo	*bp;
+
+	/* Don't bother with anything less than this */
+	/* unless we have a malloc(0) requests */
+	if (size != 0 && size < malloc_minsize)
+		size = malloc_minsize;
+
+	/* Find the right bucket */
+	if (size == 0)
+		j = 0;
+	else {
+		j = 1;
+		i = size - 1;
+		while (i >>= 1)
+			j++;
+	}
+
+	/* If it's empty, make a page more of that size chunks */
+	if (page_dir[j] == NULL && !malloc_make_chunks(j))
+		return (NULL);
+
+	bp = page_dir[j];
+
+	/* Find first word of bitmap which isn't empty */
+	for (lp = bp->bits; !*lp; lp++);
+
+	/* Find that bit, and tweak it */
+	u = 1;
+	k = 0;
+	while (!(*lp & u)) {
+		u += u;
+		k++;
+	}
+
+	if (malloc_guard) {
+		/* Walk to a random position. */
+		i = arc4random() % bp->free;
+		while (i > 0) {
+			u += u;
+			k++;
+			if (k >= MALLOC_BITS) {
+				lp++;
+				u = 1;
+				k = 0;
+			}
+#ifdef MALLOC_EXTRA_SANITY
+			if (lp - bp->bits > (bp->total - 1) / MALLOC_BITS) {
+				wrterror("chunk overflow");
+				errno = EFAULT;
+				return (NULL);
+			}
+#endif /* MALLOC_EXTRA_SANITY */
+			if (*lp & u)
+				i--;
+		}
+	}
+	*lp ^= u;
+
+	/* If there are no more free, remove from free-list */
+	if (!--bp->free) {
+		page_dir[j] = bp->next;
+		bp->next = NULL;
+	}
+	/* Adjust to the real offset of that chunk */
+	k += (lp - bp->bits) * MALLOC_BITS;
+	k <<= bp->shift;
+
+	if (malloc_junk && bp->size != 0)
+		memset((char *)bp->page + k, SOME_JUNK, (size_t)bp->size);
+
+	return ((u_char *) bp->page + k);
+}
+
+/*
+ * Magic so that malloc(sizeof(ptr)) is near the end of the page.
+ */
+#define	PTR_GAP		(malloc_pagesize - sizeof(void *))
+#define	PTR_SIZE	(sizeof(void *))
+#define	PTR_ALIGNED(p)	(((unsigned long)p & malloc_pagemask) == PTR_GAP)
+
+/*
+ * Allocate a piece of memory
+ */
+static void *
+imalloc(size_t size)
+{
+	void   *result;
+	int		ptralloc = 0;
+
+	if (!malloc_started)
+		malloc_init();
+
+	if (suicide)
+		abort();
+
+	/* does not matter if malloc_bytes fails */
+	if (px == NULL)
+		px = malloc_bytes(sizeof *px);
+
+	if (malloc_ptrguard && size == PTR_SIZE) {
+		ptralloc = 1;
+		size = malloc_pagesize;
+	}
+	if ((size + malloc_pagesize) < size) {	/* Check for overflow */
+		result = NULL;
+		errno = ENOMEM;
+	} else if (size <= malloc_maxsize)
+		result = malloc_bytes(size);
+	else
+		result = malloc_pages(size);
+
+	if (malloc_abort == 1 && result == NULL)
+		wrterror("allocation failed");
+	if (malloc_zero && result != NULL)
+		memset(result, 0, size);
+
+	if (result && ptralloc)
+		return ((char *) result + PTR_GAP);
+	return (result);
+}
+
+/*
+ * Change the size of an allocation.
+ */
+static void *
+irealloc(void *ptr, size_t size)
+{
+	void		*p;
+	size_t		osize;
+	u_long		index, i;
+	struct pginfo	**mp;
+	struct pginfo	**pd;
+	struct pdinfo	*pi;
+#ifdef	MALLOC_EXTRA_SANITY
+	u_long		pidx;
+#endif	/* MALLOC_EXTRA_SANITY */
+
+	if (suicide)
+		abort();
+
+	if (!malloc_started) {
+		wrtwarning("malloc() has never been called");
+		return (NULL);
+	}
+	if (malloc_ptrguard && PTR_ALIGNED(ptr)) {
+		if (size <= PTR_SIZE)
+			return (ptr);
+
+		p = imalloc(size);
+		if (p)
+			memcpy(p, ptr, PTR_SIZE);
+		ifree(ptr);
+		return (p);
+	}
+	index = ptr2index(ptr);
+
+	if (index < malloc_pageshift) {
+		wrtwarning("junk pointer, too low to make sense");
+		return (NULL);
+	}
+	if (index > last_index) {
+		wrtwarning("junk pointer, too high to make sense");
+		return (NULL);
+	}
+	pdir_lookup(index, &pi);
+#ifdef MALLOC_EXTRA_SANITY
+	pidx = PI_IDX(index);
+	if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+		wrterror("(ES): mapped pages not found in directory");
+		errno = EFAULT;
+		return (NULL);
+	}
+#endif /* MALLOC_EXTRA_SANITY */
+	if (pi != last_dir) {
+		prev_dir = last_dir;
+		last_dir = pi;
+	}
+	pd = pi->base;
+	mp = &pd[PI_OFF(index)];
+
+	if (*mp == MALLOC_FIRST) {	/* Page allocation */
+
+		/* Check the pointer */
+		if ((u_long) ptr & malloc_pagemask) {
+			wrtwarning("modified (page-) pointer");
+			return (NULL);
+		}
+		/* Find the size in bytes */
+		i = index;
+		if (!PI_OFF(++i)) {
+			pi = pi->next;
+			if (pi != NULL && PD_IDX(pi->dirnum) != PI_IDX(i))
+				pi = NULL;
+			if (pi != NULL)
+				pd = pi->base;
+		}
+		for (osize = malloc_pagesize;
+		    pi != NULL && pd[PI_OFF(i)] == MALLOC_FOLLOW;) {
+			osize += malloc_pagesize;
+			if (!PI_OFF(++i)) {
+				pi = pi->next;
+				if (pi != NULL && PD_IDX(pi->dirnum) != PI_IDX(i))
+					pi = NULL;
+				if (pi != NULL)
+					pd = pi->base;
+			}
+		}
+
+		if (!malloc_realloc && size <= osize &&
+		    size > osize - malloc_pagesize) {
+			if (malloc_junk)
+				memset((char *)ptr + size, SOME_JUNK, osize - size);
+			return (ptr);	/* ..don't do anything else. */
+		}
+	} else if (*mp >= MALLOC_MAGIC) {	/* Chunk allocation */
+
+		/* Check the pointer for sane values */
+		if ((u_long) ptr & ((1UL << ((*mp)->shift)) - 1)) {
+			wrtwarning("modified (chunk-) pointer");
+			return (NULL);
+		}
+		/* Find the chunk index in the page */
+		i = ((u_long) ptr & malloc_pagemask) >> (*mp)->shift;
+
+		/* Verify that it isn't a free chunk already */
+		if ((*mp)->bits[i / MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) {
+			wrtwarning("chunk is already free");
+			return (NULL);
+		}
+		osize = (*mp)->size;
+
+		if (!malloc_realloc && size <= osize &&
+		    (size > osize / 2 || osize == malloc_minsize)) {
+			if (malloc_junk)
+				memset((char *) ptr + size, SOME_JUNK, osize - size);
+			return (ptr);	/* ..don't do anything else. */
+		}
+	} else {
+		wrtwarning("irealloc: pointer to wrong page");
+		return (NULL);
+	}
+
+	p = imalloc(size);
+
+	if (p != NULL) {
+		/* copy the lesser of the two sizes, and free the old one */
+		/* Don't move from/to 0 sized region !!! */
+		if (osize != 0 && size != 0) {
+			if (osize < size)
+				memcpy(p, ptr, osize);
+			else
+				memcpy(p, ptr, size);
+		}
+		ifree(ptr);
+	}
+	return (p);
+}
+
+/*
+ * Free a sequence of pages
+ */
+static __inline__ void
+free_pages(void *ptr, u_long index, struct pginfo * info)
+{
+	u_long		i, pidx, lidx;
+	size_t		l, cachesize = 0;
+	struct pginfo	**pd;
+	struct pdinfo	*pi, *spi;
+	struct pgfree	*pf, *pt = NULL;
+	caddr_t		tail;
+
+	if (info == MALLOC_FREE) {
+		wrtwarning("page is already free");
+		return;
+	}
+	if (info != MALLOC_FIRST) {
+		wrtwarning("free_pages: pointer to wrong page");
+		return;
+	}
+	if ((u_long) ptr & malloc_pagemask) {
+		wrtwarning("modified (page-) pointer");
+		return;
+	}
+	/* Count how many pages and mark them free at the same time */
+	pidx = PI_IDX(index);
+	pdir_lookup(index, &pi);
+#ifdef MALLOC_EXTRA_SANITY
+	if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+		wrterror("(ES): mapped pages not found in directory");
+		errno = EFAULT;
+		return;
+	}
+#endif /* MALLOC_EXTRA_SANITY */
+
+	spi = pi;		/* Save page index for start of region. */
+
+	pd = pi->base;
+	pd[PI_OFF(index)] = MALLOC_FREE;
+	i = 1;
+	if (!PI_OFF(index + i)) {
+		pi = pi->next;
+		if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index + i))
+			pi = NULL;
+		else
+			pd = pi->base;
+	}
+	while (pi != NULL && pd[PI_OFF(index + i)] == MALLOC_FOLLOW) {
+		pd[PI_OFF(index + i)] = MALLOC_FREE;
+		i++;
+		if (!PI_OFF(index + i)) {
+			if ((pi = pi->next) == NULL ||
+			    PD_IDX(pi->dirnum) != PI_IDX(index + i))
+				pi = NULL;
+			else
+				pd = pi->base;
+		}
+	}
+
+	l = i << malloc_pageshift;
+
+	if (malloc_junk)
+		memset(ptr, SOME_JUNK, l);
+
+	malloc_used -= l;
+	malloc_guarded -= malloc_guard;
+	if (malloc_guard) {
+#ifdef MALLOC_EXTRA_SANITY
+		if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index + i)) {
+			wrterror("(ES): hole in mapped pages directory");
+			errno = EFAULT;
+			return;
+		}
+#endif /* MALLOC_EXTRA_SANITY */
+		pd[PI_OFF(index + i)] = MALLOC_FREE;
+		l += malloc_guard;
+	}
+	tail = (caddr_t)ptr + l;
+/*
+	if (malloc_hint)
+		madvise(ptr, l, MADV_FREE);
+
+	if (malloc_freeprot)
+		mprotect(ptr, l, PROT_NONE);
+*/
+	/* Add to free-list. */
+	if (px == NULL && (px = malloc_bytes(sizeof *px)) == NULL)
+			goto not_return;
+	px->page = ptr;
+	px->pdir = spi;
+	px->size = l;
+
+	if (free_list.next == NULL) {
+		/* Nothing on free list, put this at head. */
+		px->next = NULL;
+		px->prev = &free_list;
+		free_list.next = px;
+		pf = px;
+		px = NULL;
+	} else {
+		/*
+		 * Find the right spot, leave pf pointing to the modified
+		 * entry.
+		 */
+
+		/* Race ahead here, while calculating cache size. */
+		for (pf = free_list.next;
+		    (caddr_t)ptr > ((caddr_t)pf->page + pf->size)
+		    && pf->next != NULL;
+		    pf = pf->next)
+			cachesize += pf->size;
+
+		/* Finish cache size calculation. */
+		pt = pf;
+		while (pt) {
+			cachesize += pt->size;
+			pt = pt->next;
+		}
+
+		if ((caddr_t)pf->page > tail) {
+			/* Insert before entry */
+			px->next = pf;
+			px->prev = pf->prev;
+			pf->prev = px;
+			px->prev->next = px;
+			pf = px;
+			px = NULL;
+		} else if (((caddr_t)pf->page + pf->size) == ptr) {
+			/* Append to the previous entry. */
+			cachesize -= pf->size;
+			pf->size += l;
+			if (pf->next != NULL &&
+			    pf->next->page == ((caddr_t)pf->page + pf->size)) {
+				/* And collapse the next too. */
+				pt = pf->next;
+				pf->size += pt->size;
+				pf->next = pt->next;
+				if (pf->next != NULL)
+					pf->next->prev = pf;
+			}
+		} else if (pf->page == tail) {
+			/* Prepend to entry. */
+			cachesize -= pf->size;
+			pf->size += l;
+			pf->page = ptr;
+			pf->pdir = spi;
+		} else if (pf->next == NULL) {
+			/* Append at tail of chain. */
+			px->next = NULL;
+			px->prev = pf;
+			pf->next = px;
+			pf = px;
+			px = NULL;
+		} else {
+			wrterror("freelist is destroyed");
+			errno = EFAULT;
+			return;
+		}
+	}
+
+	if (pf->pdir != last_dir) {
+		prev_dir = last_dir;
+		last_dir = pf->pdir;
+	}
+
+	/* Return something to OS ? */
+	if (pf->size > (malloc_cache - cachesize)) {
+
+		/*
+		 * Keep the cache intact.  Notice that the '>' above guarantees that
+		 * the pf will always have at least one page afterwards.
+		 */
+		if (munmap((char *) pf->page + (malloc_cache - cachesize),
+		    pf->size - (malloc_cache - cachesize)) != 0)
+			goto not_return;
+		tail = (caddr_t)pf->page + pf->size;
+		lidx = ptr2index(tail) - 1;
+		pf->size = malloc_cache - cachesize;
+
+		index = ptr2index((caddr_t)pf->page + pf->size);
+
+		pidx = PI_IDX(index);
+		if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) >= pidx)
+			prev_dir = NULL;	/* Will be wiped out below ! */
+
+		for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx;
+		    pi = pi->next)
+			;
+
+		spi = pi;
+		if (pi != NULL && PD_IDX(pi->dirnum) == pidx) {
+			pd = pi->base;
+
+			for (i = index; i <= lidx;) {
+				if (pd[PI_OFF(i)] != MALLOC_NOT_MINE) {
+					pd[PI_OFF(i)] = MALLOC_NOT_MINE;
+#ifdef MALLOC_EXTRA_SANITY
+					if (!PD_OFF(pi->dirnum)) {
+						wrterror("(ES): pages directory underflow");
+						errno = EFAULT;
+						return;
+					}
+#endif /* MALLOC_EXTRA_SANITY */
+					pi->dirnum--;
+				}
+#ifdef MALLOC_EXTRA_SANITY
+				else
+					wrtwarning("(ES): page already unmapped");
+#endif /* MALLOC_EXTRA_SANITY */
+				i++;
+				if (!PI_OFF(i)) {
+					/*
+					 * If no page in that dir, free
+					 * directory page.
+					 */
+					if (!PD_OFF(pi->dirnum)) {
+						/* Remove from list. */
+						if (spi == pi)
+							spi = pi->prev;
+						if (pi->prev != NULL)
+							pi->prev->next = pi->next;
+						if (pi->next != NULL)
+							pi->next->prev = pi->prev;
+						pi = pi->next;
+						munmap(pd, malloc_pagesize);
+					} else
+						pi = pi->next;
+					if (pi == NULL ||
+					    PD_IDX(pi->dirnum) != PI_IDX(i))
+						break;
+					pd = pi->base;
+				}
+			}
+			if (pi && !PD_OFF(pi->dirnum)) {
+				/* Resulting page dir is now empty. */
+				/* Remove from list. */
+				if (spi == pi)	/* Update spi only if first. */
+					spi = pi->prev;
+				if (pi->prev != NULL)
+					pi->prev->next = pi->next;
+				if (pi->next != NULL)
+					pi->next->prev = pi->prev;
+				pi = pi->next;
+				munmap(pd, malloc_pagesize);
+			}
+		}
+		if (pi == NULL && malloc_brk == tail) {
+			/* Resize down the malloc upper boundary. */
+			last_index = index - 1;
+			malloc_brk = index2ptr(index);
+		}
+
+		/* XXX: We could realloc/shrink the pagedir here I guess. */
+		if (pf->size == 0) {	/* Remove from free-list as well. */
+			if (px)
+				ifree(px);
+			if ((px = pf->prev) != &free_list) {
+				if (pi == NULL && last_index == (index - 1)) {
+					if (spi == NULL) {
+						malloc_brk = NULL;
+						i = 11;
+					} else {
+						pd = spi->base;
+						if (PD_IDX(spi->dirnum) < pidx)
+							index =
+							    ((PD_IDX(spi->dirnum) + 1) *
+							    pdi_mod) - 1;
+						for (pi = spi, i = index;
+						    pd[PI_OFF(i)] == MALLOC_NOT_MINE;
+						    i--)
+#ifdef MALLOC_EXTRA_SANITY
+							if (!PI_OFF(i)) {
+								pi = pi->prev;
+								if (pi == NULL || i == 0)
+									break;
+								pd = pi->base;
+								i = (PD_IDX(pi->dirnum) + 1) * pdi_mod;
+							}
+#else /* !MALLOC_EXTRA_SANITY */
+						{
+						}
+#endif /* MALLOC_EXTRA_SANITY */
+						malloc_brk = index2ptr(i + 1);
+					}
+					last_index = i;
+				}
+				if ((px->next = pf->next) != NULL)
+					px->next->prev = px;
+			} else {
+				if ((free_list.next = pf->next) != NULL)
+					free_list.next->prev = &free_list;
+			}
+			px = pf;
+			last_dir = prev_dir;
+			prev_dir = NULL;
+		}
+	}
+not_return:
+	if (pt != NULL)
+		ifree(pt);
+}
+
+/*
+ * Free a chunk, and possibly the page it's on, if the page becomes empty.
+ */
+
+/* ARGSUSED */
+static __inline__ void
+free_bytes(void *ptr, u_long index __attribute__((unused)), 
+           struct pginfo * info)
+{
+	struct pginfo	**mp, **pd;
+	struct pdinfo	*pi;
+#ifdef	MALLOC_EXTRA_SANITY
+	u_long		pidx;
+#endif	/* MALLOC_EXTRA_SANITY */
+	void		*vp;
+	long		i;
+
+	/* Find the chunk number on the page */
+	i = ((u_long) ptr & malloc_pagemask) >> info->shift;
+
+	if ((u_long) ptr & ((1UL << (info->shift)) - 1)) {
+		wrtwarning("modified (chunk-) pointer");
+		return;
+	}
+	if (info->bits[i / MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) {
+		wrtwarning("chunk is already free");
+		return;
+	}
+	if (malloc_junk && info->size != 0)
+		memset(ptr, SOME_JUNK, (size_t)info->size);
+
+	info->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS);
+	info->free++;
+
+	if (info->size != 0)
+		mp = page_dir + info->shift;
+	else
+		mp = page_dir;
+
+	if (info->free == 1) {
+		/* Page became non-full */
+
+		/* Insert in address order */
+		while (*mp != NULL && (*mp)->next != NULL &&
+		    (*mp)->next->page < info->page)
+			mp = &(*mp)->next;
+		info->next = *mp;
+		*mp = info;
+		return;
+	}
+	if (info->free != info->total)
+		return;
+
+	/* Find & remove this page in the queue */
+	while (*mp != info) {
+		mp = &((*mp)->next);
+#ifdef MALLOC_EXTRA_SANITY
+		if (!*mp) {
+			wrterror("(ES): Not on queue");
+			errno = EFAULT;
+			return;
+		}
+#endif /* MALLOC_EXTRA_SANITY */
+	}
+	*mp = info->next;
+
+	/* Free the page & the info structure if need be */
+	pdir_lookup(ptr2index(info->page), &pi);
+#ifdef MALLOC_EXTRA_SANITY
+	pidx = PI_IDX(ptr2index(info->page));
+	if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+		wrterror("(ES): mapped pages not found in directory");
+		errno = EFAULT;
+		return;
+	}
+#endif /* MALLOC_EXTRA_SANITY */
+	if (pi != last_dir) {
+		prev_dir = last_dir;
+		last_dir = pi;
+	}
+	pd = pi->base;
+	pd[PI_OFF(ptr2index(info->page))] = MALLOC_FIRST;
+
+	/* If the page was mprotected, unprotect it before releasing it */
+	if (info->size == 0)
+		mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE);
+
+	vp = info->page;	/* Order is important ! */
+	if (vp != (void *) info)
+		ifree(info);
+	ifree(vp);
+}
+
+static void
+ifree(void *ptr)
+{
+	struct pginfo	*info, **pd;
+	u_long		index;
+#ifdef	MALLOC_EXTRA_SANITY
+	u_long		pidx;
+#endif	/* MALLOC_EXTRA_SANITY */
+	struct pdinfo	*pi;
+
+	if (!malloc_started) {
+		wrtwarning("malloc() has never been called");
+		return;
+	}
+	/* If we're already sinking, don't make matters any worse. */
+	if (suicide)
+		return;
+
+	if (malloc_ptrguard && PTR_ALIGNED(ptr))
+		ptr = (char *) ptr - PTR_GAP;
+
+	index = ptr2index(ptr);
+
+	if (index < malloc_pageshift) {
+		warnx("(%p)", ptr);
+		wrtwarning("ifree: junk pointer, too low to make sense");
+		return;
+	}
+	if (index > last_index) {
+		warnx("(%p)", ptr);
+		wrtwarning("ifree: junk pointer, too high to make sense");
+		return;
+	}
+	pdir_lookup(index, &pi);
+#ifdef MALLOC_EXTRA_SANITY
+	pidx = PI_IDX(index);
+	if (pi == NULL || PD_IDX(pi->dirnum) != pidx) {
+		wrterror("(ES): mapped pages not found in directory");
+		errno = EFAULT;
+		return;
+	}
+#endif /* MALLOC_EXTRA_SANITY */
+	if (pi != last_dir) {
+		prev_dir = last_dir;
+		last_dir = pi;
+	}
+	pd = pi->base;
+	info = pd[PI_OFF(index)];
+
+	if (info < MALLOC_MAGIC)
+		free_pages(ptr, index, info);
+	else
+		free_bytes(ptr, index, info);
+
+	/* does not matter if malloc_bytes fails */
+	if (px == NULL)
+		px = malloc_bytes(sizeof *px);
+
+	return;
+}
+
+/*
+ * Common function for handling recursion.  Only
+ * print the error message once, to avoid making the problem
+ * potentially worse.
+ */
+static void
+malloc_recurse(void)
+{
+	static int	noprint;
+
+	if (noprint == 0) {
+		noprint = 1;
+		wrtwarning("recursive call");
+	}
+	malloc_active--;
+	_MALLOC_UNLOCK();
+	errno = EDEADLK;
+}
+
+/*
+ * These are the public exported interface routines.
+ */
+void *
+malloc(size_t size)
+{
+	void		*r;
+
+	_MALLOC_LOCK();
+	malloc_func = " in malloc():";
+	if (malloc_active++) {
+		malloc_recurse();
+		return (NULL);
+	}
+	r = imalloc(size);
+	UTRACE(0, size, r);
+	malloc_active--;
+	_MALLOC_UNLOCK();
+	if (malloc_xmalloc && r == NULL) {
+		wrterror("out of memory");
+		errno = ENOMEM;
+	}
+	return (r);
+}
+
+void
+free(void *ptr)
+{
+	/* This is legal. XXX quick path */
+	if (ptr == NULL)
+		return;
+
+	_MALLOC_LOCK();
+	malloc_func = " in free():";
+	if (malloc_active++) {
+		malloc_recurse();
+		return;
+	}
+	ifree(ptr);
+	UTRACE(ptr, 0, 0);
+	malloc_active--;
+	_MALLOC_UNLOCK();
+	return;
+}
+
+void *
+realloc(void *ptr, size_t size)
+{
+	void		*r;
+
+	_MALLOC_LOCK();
+	malloc_func = " in realloc():";
+	if (malloc_active++) {
+		malloc_recurse();
+		return (NULL);
+	}
+
+	if (ptr == NULL)
+		r = imalloc(size);
+	else
+		r = irealloc(ptr, size);
+
+	UTRACE(ptr, size, r);
+	malloc_active--;
+	_MALLOC_UNLOCK();
+	if (malloc_xmalloc && r == NULL) {
+		wrterror("out of memory");
+		errno = ENOMEM;
+	}
+	return (r);
+}
+
+void *calloc(size_t n, size_t size)
+{
+  void *ptr = malloc(n * size);
+  bzero(ptr, n * size);
+  return ptr;
+}
diff --git a/firmware/memchr.c b/firmware/memchr.c
new file mode 100644
index 0000000..ea47cdc
--- /dev/null
+++ b/firmware/memchr.c
@@ -0,0 +1,20 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+void  *memchr(const void *s, int c, size_t n)
+{
+  size_t i;
+
+  for(i = 0; i < n; i++)
+    if(((unsigned char*)s)[i] == (unsigned char)c)
+      return (void*)((unsigned long)s + i);
+
+  return NULL;
+}
+
diff --git a/firmware/memcmp.c b/firmware/memcmp.c
new file mode 100644
index 0000000..4588f7c
--- /dev/null
+++ b/firmware/memcmp.c
@@ -0,0 +1,23 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+   unsigned char *str1 = (void*)s1;
+   unsigned char *str2 = (void*)s2;
+   size_t pos;
+
+   for(pos = 0; pos < n; pos++) {
+     if(str1[pos] < str2[pos])
+       return 1;
+     if(str1[pos] > str2[pos])
+       return -1;
+   }
+   return 0;
+}
+
diff --git a/firmware/memcpy.c b/firmware/memcpy.c
new file mode 100644
index 0000000..7b91ed3
--- /dev/null
+++ b/firmware/memcpy.c
@@ -0,0 +1,19 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+void  *memcpy(void *dest, const void *src, size_t count)
+{
+  /* This would be a prime candidate for reimplementation in assembly */
+  char *in_src = (char*)src;
+  char *in_dest = (char*)dest;
+
+  while(count--)
+    *in_dest++ = *in_src++;
+  return dest;
+}
+
diff --git a/firmware/memmove.c b/firmware/memmove.c
new file mode 100644
index 0000000..1f716b1
--- /dev/null
+++ b/firmware/memmove.c
@@ -0,0 +1,22 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+void  *memmove(void *dest, const void *src, size_t n)
+{
+  void *d0 = dest;
+  char *d = (char *) dest;
+  char *s = (char *) src;
+  if (s < d)
+    for (s += n, d += n; 0 != n; --n)
+      *--d = *--s;
+  else if (s != d)
+    for (; 0 != n; --n)
+      *d++ = *s++;
+  return d0;
+}
+
diff --git a/firmware/memset.c b/firmware/memset.c
new file mode 100644
index 0000000..22fa41c
--- /dev/null
+++ b/firmware/memset.c
@@ -0,0 +1,18 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+void  *memset(void *s,int c, size_t count)
+{
+  char *xs = (char *) s;
+
+  while (count--)
+    *xs++ = c;
+
+  return s;
+}
+
diff --git a/firmware/strcasecmp.c b/firmware/strcasecmp.c
new file mode 100644
index 0000000..15862bb
--- /dev/null
+++ b/firmware/strcasecmp.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <ctype.h>
+#include <strings.h>
+
+int strcasecmp(const char *s1, const char *s2)
+{
+  int result;
+
+  while (1) {
+    result = tolower(*s1) - tolower(*s2);
+    if (result != 0 || *s1 == '\0')
+      break;
+
+    ++s1;
+    ++s2;
+  }
+
+  return result;
+}
diff --git a/firmware/strchr.c b/firmware/strchr.c
new file mode 100644
index 0000000..e2ec632
--- /dev/null
+++ b/firmware/strchr.c
@@ -0,0 +1,20 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+char *strchr(const char *s, int c)
+{
+  const char *cur;
+  for (cur = s; *cur; cur++) {
+    if (*cur == c) {
+      return ((char*)cur);
+    }
+  }
+
+  return NULL;
+}
diff --git a/firmware/strcmp.c b/firmware/strcmp.c
new file mode 100644
index 0000000..637b34f
--- /dev/null
+++ b/firmware/strcmp.c
@@ -0,0 +1,18 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+int strcmp(const char *cs, const char *ct)
+{
+  register signed char __res;
+
+  while (1) {
+    if ((__res = *cs - *ct++) != 0 || !*cs++)
+      break;
+  }
+  return __res;
+}
diff --git a/firmware/strcpy.c b/firmware/strcpy.c
new file mode 100644
index 0000000..d81b398
--- /dev/null
+++ b/firmware/strcpy.c
@@ -0,0 +1,17 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+char *strcpy(char *dest, const char *src)
+{
+  char *retval = dest;
+  // FIXME: Make higher-speed version?
+  while(*src) *dest++ = *src++;
+  return retval;
+}
+
diff --git a/firmware/strdup.c b/firmware/strdup.c
new file mode 100644
index 0000000..cbab5ed
--- /dev/null
+++ b/firmware/strdup.c
@@ -0,0 +1,17 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+char *strdup(const char *s)
+{
+  size_t bufsize = strlen(s) + 1;
+  char *retval = malloc(bufsize);
+  if(retval) memcpy(retval, s, bufsize);
+  return retval;
+}
+
diff --git a/firmware/strerror.c b/firmware/strerror.c
new file mode 100644
index 0000000..419c076
--- /dev/null
+++ b/firmware/strerror.c
@@ -0,0 +1,21 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+char  *strerror(int errnum)
+{
+  static char buf[1024];
+  switch(errnum) {
+    case EBADF: return "Bad file.\n";
+    case EACCES: return "Access prohibited.\n";
+  }
+  sprintf(buf, "Unkown error: %d", errnum);
+  return buf;
+}
+
diff --git a/firmware/string.c b/firmware/string.c
deleted file mode 100644
index 2e7aee6..0000000
--- a/firmware/string.c
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "string.h"
-
-/*
- * A small implementation of strcpy
- */
-
-void strcpy(char *s2, char *s1)
-{
-    while(*s1)
-    {
-        *s2 = *s1;
-        s1++;
-        s2++;
-    }
-}
-
-// A utility function to reverse a string
-void reverse(char str[], int length)
-{
-    int start = 0;
-    int end = length - 1;
-    while (start < end) {
-        char temp = str[start];
-        str[start] = str[end];
-        str[end] = temp;
-        end--;
-        start++;
-    }
-}
-
-/*
- * Implementation of citoa()
- * 
- * Converts a string into a signed integer
- */
-int atoi(char* str, int base)
-{
-    int result = 0;
-    int sign = 1;
-    int i = 0;
-
-    // Check for leading sign character
-    if (str[0] == '-') {
-        sign = -1;
-        i++;
-    }
-    else if (str[0] == '+') {
-        i++;
-    }
-
-    // Convert digits to integer value
-    while (str[i] != '\0') {
-        if (str[i] < '0' || str[i] > (base+'0') ) {
-            break;
-        }
-        result = result * base + (str[i] - '0');
-        i++;
-    }
-
-    return sign * result;
-}
-
-/*
- * Implementation of citoa()
- * 
- * Converts an integer into a string
- */
-char* citoa(int num, char* str, int base)
-{
-    int i = 0;
-    bool isNegative = false;
- 
-    /* Handle 0 explicitly, otherwise empty string is
-     * printed for 0 */
-    if (num == 0) {
-        str[i++] = '0';
-        str[i] = '\0';
-        return str;
-    }
- 
-    // In standard itoa(), negative numbers are handled
-    // only with base 10. Otherwise numbers are
-    // considered unsigned.
-    if (num < 0 && base == 10) {
-        isNegative = true;
-        num = -num;
-    }
- 
-    // Process individual digits
-    while (num != 0) {
-        int rem = num % base;
-        str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
-        num = num / base;
-    }
- 
-    // If number is negative, append '-'
-    if (isNegative)
-        str[i++] = '-';
- 
-    str[i] = '\0'; // Append string terminator
- 
-    // Reverse the string
-    reverse(str, i);
- 
-    return str;
-}
-
-/*
- * Compare strings
- * 0: if strings are equal
- * 0: if the first non-matching character in str1 is greater (in ASCII) than that of str2.
- * 0: if the first non-matching character in str1 is lower (in ASCII) than that of str2.
- */
-
-int strcmp(const char* s1, const char* s2)
-{
-    while(*s1 && (*s1 == *s2))
-    {
-        s1++;
-        s2++;
-    }
-    return *(const unsigned char*)s1 - *(const unsigned char*)s2;
-}
diff --git a/firmware/strlen.c b/firmware/strlen.c
new file mode 100644
index 0000000..166b1f7
--- /dev/null
+++ b/firmware/strlen.c
@@ -0,0 +1,17 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+size_t strlen(const char *s)
+{
+  const char *sc;
+
+  for (sc = s; *sc != '\0'; ++sc)
+    /* nothing */;
+  return sc - s;
+}
+
diff --git a/firmware/strncmp.c b/firmware/strncmp.c
new file mode 100644
index 0000000..780dd4d
--- /dev/null
+++ b/firmware/strncmp.c
@@ -0,0 +1,19 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+int strncmp(const char *str1, const char *str2, size_t count) 
+{
+  register signed char __res = 0;
+
+  while(count--) {
+    if ((__res = *str1 - *str2) != 0 || !*str1++ || !*str2++)
+      break;
+  }
+  return __res;
+}
+
diff --git a/firmware/strncpy.c b/firmware/strncpy.c
new file mode 100644
index 0000000..21daee4
--- /dev/null
+++ b/firmware/strncpy.c
@@ -0,0 +1,16 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+char  *strncpy(char *dest, const char *src, size_t count)
+{
+  char *tmp = dest;
+  while (count-- && (*dest++ = *src++) != '\0')
+    /* nothing */;
+  return tmp;
+}
+
diff --git a/firmware/strnlen.c b/firmware/strnlen.c
new file mode 100644
index 0000000..9d422c8
--- /dev/null
+++ b/firmware/strnlen.c
@@ -0,0 +1,17 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+
+size_t strnlen(const char *s, size_t count)
+{
+  const char *sc;
+
+  for (sc = s; count-- && *sc != '\0'; ++sc)
+    /* nothing */;
+  return sc - s;
+}
+
diff --git a/firmware/strrchr.c b/firmware/strrchr.c
new file mode 100644
index 0000000..e528976
--- /dev/null
+++ b/firmware/strrchr.c
@@ -0,0 +1,19 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+char  *strrchr(const char *s, int c)
+{
+  char *retval = NULL;
+  const char *cur;
+  for(cur = s; *cur; cur++)
+    if(*cur == c)
+      retval = (char*)cur;
+  return retval;
+}
+
diff --git a/firmware/strstr.c b/firmware/strstr.c
new file mode 100644
index 0000000..480112c
--- /dev/null
+++ b/firmware/strstr.c
@@ -0,0 +1,24 @@
+/* 
+ * Copyright (C) 2014, Galois, Inc.
+ * This sotware is distributed under a standard, three-clause BSD license.
+ * Please see the file LICENSE, distributed with this software, for specific
+ * terms and conditions.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+char *strstr(const char *str1, const char *str2)
+{
+  size_t len_str2 = strlen(str2);
+  char *cur;
+
+  for (cur = (char*)str1; cur != NULL; cur = strchr(cur, *str2)) {
+    if (!strncmp(cur, str2, len_str2)) {
+      break;
+    }
+    cur++;
+  }
+
+  return cur;
+}
+
diff --git a/firmware/strtol.c b/firmware/strtol.c
new file mode 100644
index 0000000..a338475
--- /dev/null
+++ b/firmware/strtol.c
@@ -0,0 +1,142 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <errno.h>
+
+#define isdigit(c) (c >= '0' && c <= '9')
+
+long int strtol(const char *nptr, char **endptr, int base)
+{
+  /* Taken from the NetBSD libc implementation. As per the license, here's
+   * the copyright notice & etc. attached to it:
+   *
+   * Copyright (c) 1990, 1993
+   *  The Regents of the University of California.  All rights reserved.
+   *  Redistribution and use in source and binary forms, with or without
+   *  modification, are permitted provided that the following conditions
+   *  are met:
+   *  1. Redistributions of source code must retain the above copyright
+   *     notice, this list of conditions and the following disclaimer.
+   *  2. Redistributions in binary form must reproduce the above copyright
+   *     notice, this list of conditions and the following disclaimer in the
+   *     documentation and/or other materials provided with the distribution.
+   *  3. Neither the name of the University nor the names of its contributors
+   *     may be used to endorse or promote products derived from this software
+   *     without specific prior written permission.
+   * 
+   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+   * SUCH DAMAGE.
+   */
+#define isalpha(c)  (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))
+#define isupper(c)  ((c >= 'A') && (c <= 'Z'))
+#define isspace(c)  ((c == ' ') || (c == '\n') || (c == '\t'))
+  const char *s;
+  long acc, cutoff;
+  int c;
+  int neg, any, cutlim;
+
+  assert(nptr != NULL);
+  /* endptr may be NULL */
+
+  /*
+   * Skip white space and pick up leading +/- sign if any.
+   * If base is 0, allow 0x for hex and 0 for octal, else
+   * assume decimal; if base is already 16, allow 0x.
+   */
+  s = nptr;
+  do {
+    c = (unsigned char) *s++;
+  } while (isspace(c));
+  if (c == '-') {
+    neg = 1;
+    c = *s++;
+  } else {
+    neg = 0;
+    if (c == '+')
+      c = *s++;
+  } 
+  if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
+    c = s[1];
+    s += 2;
+    base = 16;
+  }
+  if (base == 0)
+    base = c == '0' ? 8 : 10;
+
+  /*
+   * Compute the cutoff value between legal numbers and illegal
+   * numbers.  That is the largest legal value, divided by the
+   * base.  An input number that is greater than this value, if
+   * followed by a legal input character, is too big.  One that
+   * is equal to this value may be valid or not; the limit
+   * between valid and invalid numbers is then based on the last
+   * digit.  For instance, if the range for longs is
+   * [-2147483648..2147483647] and the input base is 10,
+   * cutoff will be set to 214748364 and cutlim to either
+   * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+   * a value > 214748364, or equal but the next digit is > 7 (or 8),
+   * the number is too big, and we will return a range error.
+   *
+   * Set any if any `digits' consumed; make it negative to indicate
+   * overflow.
+   */
+  cutoff = neg ? LONG_MIN : LONG_MAX;
+  cutlim = (int)(cutoff % base);
+  cutoff /= base;
+  if (neg) {
+    if (cutlim > 0) {
+      cutlim -= base;
+      cutoff += 1;
+    }
+    cutlim = -cutlim;
+  }
+  for (acc = 0, any = 0;; c = (unsigned char) *s++) {
+    if (isdigit(c))
+      c -= '0';
+    else if (isalpha(c))
+      c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+    else
+      break;
+    if (c >= base)
+      break;
+    if (any < 0)
+      continue;
+    if (neg) {
+      if (acc < cutoff || (acc == cutoff && c > cutlim)) {
+        any = -1;
+        acc = LONG_MIN;
+        errno = ERANGE;
+      } else {
+        any = 1;
+        acc *= base;
+        acc -= c;
+      }
+    } else {
+      if (acc > cutoff || (acc == cutoff && c > cutlim)) {
+        any = -1;
+        acc = LONG_MAX;
+        errno = ERANGE;
+      } else {
+        any = 1;
+        acc *= base;
+        acc += c;
+      }
+    }
+  }
+  if (endptr != 0)
+    *endptr = (char*)(any ? s - 1 : nptr);
+  return (acc);
+}
+
+unsigned long long strtoull (const char*__restrict nptr, char **__restrict endptr, int base) {
+    return (unsigned long long) strtol(nptr, endptr, base);
+    // HACK: Hope it won't break things severely before I change to musl
+}
-- 
GitLab