diff --git a/include/kernel/ar.h b/include/kernel/ar.h
new file mode 100644
index 0000000000000000000000000000000000000000..e6bf8b00fa1a0c51b68db14f51dd71d31feadcef
--- /dev/null
+++ b/include/kernel/ar.h
@@ -0,0 +1,57 @@
+#ifndef _KERNEL_AR_H_
+#define _KERNEL_AR_H_
+
+#include <stdint.h>
+
+#define AR_MAG   "!<arch>\n"		/* AR MAGIC ID */
+#define AR_FMAG  "`\n"			/* AR HDR END MAGIC */
+
+#define AR_STR_TERM		'/'
+#define GNU_AR_STR_TBL_MAG	"//"	/* marks a gnu archive string table */
+#define GNU_AR_SYM_TBL_MAG	"/ "	/* marks a gnu archive symbol table */
+
+
+struct ar_hdr {
+	char ar_name[16];	/* ar file identifier */
+	char ar_date[12];	/* file date, dec seconds since epoch */
+	char ar_uid[6];		/* user  ID; dec  */
+	char ar_gid[6];		/* group ID; dec  */
+	char ar_mode[8];	/* file mode; oct */
+	char ar_size[10];	/* file size; dec */
+	char ar_fmag[2];	/* AR_FMAG (0x60, 0x0a) */
+};
+
+
+/**
+ * The archive reference structure.
+ */
+
+struct archive {
+	char  *ar_base;		/* the archive's memory location */
+	unsigned long ar_size;	/* the size of the archive in bytes */
+	char  *ar_str;		/* the archive's string table */
+
+	unsigned int n_file;	/* the number of files in the archive */
+	char **fname;		/* pointer array to the file name strings */
+	char **p_file;		/* pointer array to the file(s) */
+	unsigned long *filesz;	/* the size of each file */
+	unsigned long *fnamesz;	/* the size of each file name */
+
+	uint32_t n_sym;		/* the number of symbols in the index */
+	char **sym;		/* pointer array to the symbol name strings */
+	void **p_sym;		/* pointer array to the file a symbol is in */
+
+};
+
+
+void ar_list_files(struct archive *a);
+void *ar_find_file(struct archive *a, const char *name);
+void *ar_find_symbol(struct archive *a, const char *name);
+void ar_free(struct archive *a);
+int ar_load(char *p, unsigned long size, struct archive *a);
+
+
+
+
+#endif /* _KERNEL_AR_H_ */
+
diff --git a/init/Kconfig b/init/Kconfig
index 5cd3ad80058f6e9d30e90633f538e6ab30631121..3159f69b9336563d47719078417eb6adc743c052 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -47,6 +47,7 @@ endmenu # "General Setup"
 
 menuconfig MODULES
 	bool "Enable loadable module support"
+	depends on AR
 	option modules
 	help
 	  Kernel modules are small pieces of compiled code which can
diff --git a/kernel/module.c b/kernel/module.c
index 96c95d9202552485da2f7cc28061f29015668a90..46eeebfb11b83ebb9dab103ea604caf359faa118 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -8,9 +8,9 @@
 #include <kernel/ksym.h>
 
 
-/* XXX quick and dirty malloc() standin */
+/* XXX quick and dirty kmalloc() standin */
 #include <page.h>
-void *malloc(size_t size) {
+static void *kmalloc(size_t size) {
 	return page_alloc();
 }
 
@@ -675,7 +675,7 @@ static int module_load_mem(struct elf_module *m)
 	void *mem;
 
 
-	mem = malloc(m->size);
+	mem = kmalloc(m->size);
 
 	if (!mem)
 		return -ENOMEM;
@@ -692,7 +692,7 @@ static int module_load_mem(struct elf_module *m)
 
 	m->num_sec = get_num_alloc_sections(m);
 	m->sec = (struct module_section *)
-		 malloc(sizeof(struct module_section) * m->num_sec);
+		 kmalloc(sizeof(struct module_section) * m->num_sec);
 
 	if (!m->sec)
 		return -1;
@@ -715,7 +715,7 @@ static int module_load_mem(struct elf_module *m)
 		s->size = sec->sh_size;
 
 		src = get_shstrtab_str(m, idx);
-		s->name = malloc(strlen(src));
+		s->name = kmalloc(strlen(src));
 
 		if (!s->name)
 			return -1;
diff --git a/lib/Kconfig b/lib/Kconfig
index 1fbbde01e989968accfe7c9d9ad15f7571fe3e33..c504a517ddb3f547f9c11a470c2e95afbdfc8acb 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -80,7 +80,12 @@ config PAGE_MAP_MOVE_NODE_AVAIL_THRESH
 	  When page map node run out of pages, they are moved to a list of
 	  empty nodes until a number of pages are freed, defined by the
 	  threshold above. If unsure, use a threshold of 1.
-
 endmenu
 
+config AR
+	bool "AR archive loading support"
+	default y
+	help
+	  Enable loading of SVR4/GNU style AR archives.
+
 endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 7e73f3a1c25e3eccafaefade80a75d9fd20be178..4faa3d468b88255cc6f03755a2ae3067a0714a8b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,3 +1,4 @@
 lib-$(CONFIG_SYSCTL)	+= sysctl.o
 lib-$(CONFIG_MM)	+= mm.o
 lib-$(CONFIG_PAGE_MAP)	+= page.o
+lib-$(CONFIG_AR)	+= ar.o
diff --git a/lib/ar.c b/lib/ar.c
new file mode 100644
index 0000000000000000000000000000000000000000..3d3fca5734abbc24dcd237cf5d4b38f194322872
--- /dev/null
+++ b/lib/ar.c
@@ -0,0 +1,607 @@
+/**
+ *
+ * This is a very simple AR reader. It expects indexed SVR4/GNU type archive,
+ * i.e. created invoking: ar rcs <archive> <files...>
+ * in the following format:
+ *
+ * !<arch><ar header><SVR4/GNU symbol table><entries...>
+ *
+ * Note that there are only basic checks for actual file size. This really
+ * depends on uncorrupted files, so you might want to verify them ahead of
+ * calling this.
+ *
+ *
+ * @note this does produce references to the parsed file, rather than create a
+ *       copy
+ *
+ * @note The sym array in struct archive may be used to lookup a module
+ *       dependency when resolving symbols.
+ */
+
+
+
+#include <stdlib.h>	/* strtol() */
+#include <string.h>	/* strncmp() */	
+
+#include <kernel/ar.h>
+#include <kernel/printk.h>
+
+
+
+
+/* XXX quick and dirty kmalloc() standin */
+#include <page.h>
+static void *kmalloc(size_t size) {
+	return page_alloc();
+}
+
+static void kfree(void *ptr) {
+	page_free(ptr);
+}
+
+
+/* XXX cleanup*/
+#define PLATFORM_IS_LITTLE_ENDIAN 0
+
+
+#if PLATFORM_IS_LITTLE_ENDIAN
+/**
+ * @brief byte swap unsigned int
+ */
+
+static uint32_t swap_uint32(uint32_t x)
+{
+	x = ((x << 8) & 0xFF00FF00UL) | ((x >> 8) & 0x00FF00FFUL);
+
+	return (x << 16) | (x >> 16);
+}
+#endif
+
+/**
+ * @brief verify ar file header magic
+ *
+ * @param p a buffer to the start of the file buffer
+ *
+ * @return pointer to first header, NULL if no match
+ */
+
+static char *ar_check_magic(char *p)
+{
+	if (strncmp(p, AR_MAG, strlen(AR_MAG)))
+		return NULL;
+
+	return p + strlen(AR_MAG);
+}
+
+
+/**
+ * @brief reads an ar header
+ *
+ * @param p a pointer to the next header record
+ * @param h a struct ar_hdr
+ *
+ * @return pointer to next record
+ */
+
+static char *ar_read_hdr(char *p, struct ar_hdr *hdr)
+{
+	memcpy(hdr, p, sizeof(struct ar_hdr));
+
+	return p + sizeof(struct ar_hdr);
+}
+
+/**
+ * @brief locate the associated string table entry for an oversized file name
+ *
+ * @param a a struct archive
+ * @param hdr a struct ar_hdr
+ *
+ * @return the pointer to the string entry or NULL if not found
+ */
+
+static char *ar_get_strtbl_filename(struct archive *a, struct ar_hdr *hdr)
+{
+	long off;
+
+	char *str;
+
+
+	off =  strtol(&hdr->ar_name[1], NULL, 10);
+
+
+	str = (char *) a->ar_str + off;
+
+	if (str > (a->ar_base + a->ar_size))
+		str = NULL;
+
+	if (str < a->ar_base)
+		str = NULL;
+
+	return str;
+}
+
+
+/**
+ * @brief get string table reference
+ *
+ * @param a
+ *
+ * @return the converted value
+ */
+
+static long int ar_get_entry_size(struct ar_hdr *hdr)
+{
+	return strtol(hdr->ar_size, NULL, sizeof(hdr->ar_size));
+}
+
+
+/**
+ * @brief process a SVR4/GNU archive symbol table
+ *
+ * @param p a pointer to the next record
+ * @param a a struct archive
+ * @param hdr a struct ar_hdr
+ *
+ * @return pointer to next record or NULL on error
+ */
+
+/* XXX: #define PLATFORM_IS_LITTLE_ENDIAN 1 */
+static char *ar_process_sym_tbl(char *p, struct archive *a, struct ar_hdr *hdr)
+{
+	unsigned long i, j;
+
+	long int recsize;
+	long int strsize;
+
+	uint32_t *symoff;
+
+
+	if (a->n_sym) {
+		pr_err("[AR]: Error, more than one symbol table in file.\n");
+		return NULL;
+	}
+
+
+	recsize = ar_get_entry_size(hdr);
+
+	if ((p + recsize)  > (a->ar_base + a->ar_size)) {
+		pr_err("[AR]: Error, symbol table header size is foul.\n");
+		return NULL;
+	}
+
+	pr_debug("[AR]: Symbol table entry size is %d\n", recsize);
+
+	/* the symbol count is 4 bytes wide, MSB first */
+	memcpy(&a->n_sym, p, sizeof(uint32_t));
+#if PLATFORM_IS_LITTLE_ENDIAN
+	a->n_sym = swap_uint32(a->n_sym);
+#endif
+
+
+	p += sizeof(a->n_sym);	/* move to symbol offsets */
+
+
+	a->p_sym = (typeof(a->p_sym)) kmalloc(a->n_sym * sizeof(a->p_sym));
+
+	if (!a->p_sym) {
+		pr_err("[AR]: Error allocating symbol start array\n");
+		goto error;
+	}
+
+
+	/* symbol offset array entries are 4 bytes wide, MSB first */
+	symoff = (uint32_t *) p;
+
+	/* if the array is not word-aligned, we are boned... */
+	/* XXX: not needed as long as we can handle unaligned access traps */
+	if (((long) symoff) & (sizeof(uint32_t) - 1)) {
+		pr_err("[AR]: Error, symbol offset array not aligned\n");
+		goto error;
+	}
+
+	/* rather than the entry header, set the symbol start pointers to the
+	 * beginning of the file they are in
+	 */
+	for (i = 0; i < a->n_sym; i++) {
+#if PLATFORM_IS_LITTLE_ENDIAN
+		a->p_sym[i] = (void *) a->ar_base + swap_uint32(symoff[i])
+			+ sizeof(struct ar_hdr);
+#else
+		a->p_sym[i] = (void *) a->ar_base + symoff[i]
+			+ sizeof(struct ar_hdr);
+#endif
+	}
+
+
+	p += a->n_sym * sizeof(uint32_t);	/* move to symbol strings */
+
+
+	/* size of string section is
+	 * record size - symbol count entry - symbol offsets array
+	 */
+	strsize = recsize - sizeof(uint32_t) - (a->n_sym * sizeof(uint32_t));
+
+	pr_debug("[AR]: Symbol string table size is %d\n", strsize);
+
+	a->sym = (typeof(a->sym)) kmalloc(a->n_sym * sizeof(a->sym));
+
+	if (!a->sym) {
+		pr_err("[AR]: Error allocating symbol reference array\n");
+		goto error;
+	}
+
+
+	a->sym[0] = &p[0];	/* first string */
+
+	for (i = 1, j = 1; (i < strsize) && (j < a->n_sym); i++) {
+		if (p[i] == '\0')
+			a->sym[j++] = &p[i + 1];
+	}
+
+	
+	return p + strsize;
+
+error:
+	return NULL;
+}
+
+
+/**
+ * @brief process a string table record
+ *
+ * @param p a pointer to the next record
+ * @param a a struct archive
+ * @param hdr a struct ar_hdr
+ *
+ * @return pointer to next record or NULL on error
+ */
+
+static char *ar_process_strtbl(char *p, struct archive *a, struct ar_hdr *hdr)
+{
+
+	if (a->ar_str) {
+		pr_err("[AR]: Error, more than one string table in file.\n");
+		return NULL;
+	}
+
+	a->ar_str = (typeof(a->ar_str)) p;
+
+	return p + ar_get_entry_size(hdr);
+}
+
+
+/**
+ * @brief process a file record
+ *
+ * @param p a pointer to the next record
+ * @param a a struct archive
+ * @param hdr a struct ar_hdr
+ *
+ * @return pointer to next record or NULL on error
+ */
+
+static char *ar_process_file(char *p, struct archive *a, struct ar_hdr *hdr)
+{
+	char *str;
+
+
+	str = (char *) (p - sizeof(struct ar_hdr));
+
+	/* name is an offset into the string table */
+	if (str[0] == '/')
+		str = ar_get_strtbl_filename(a, hdr);
+
+
+	/* set file name, start and sizes */
+	a->fname[a->n_file] = (typeof((*a->fname))) str;
+
+
+	a->p_file[a->n_file] = (typeof((*a->p_file))) p;
+
+	a->filesz[a->n_file] = ar_get_entry_size(hdr);
+
+	/* all normal strings terminate with "/" */
+	a->fnamesz[a->n_file] = (unsigned int) (strchr(str, AR_STR_TERM) - str);
+
+	/* ajdust to next record */
+	p += a->filesz[a->n_file];
+
+	a->n_file++;	/* next file */
+
+	return p;
+}
+
+
+/**
+ * @brief process an archive header
+ *
+ * @param p a pointer to the next record
+ * @param a a struct archive
+ * @param hdr a struct ar_hdr
+ *
+ * @return pointer to next record or NULL on error
+ */
+
+static char *ar_process_hdr(char *p, struct archive *a, struct ar_hdr *hdr)
+{
+	int ret;
+
+
+	/* check if record is symbol index */
+	ret = strncmp(hdr->ar_name, GNU_AR_STR_TBL_MAG,
+		      strlen(GNU_AR_STR_TBL_MAG));
+	if (!ret) {
+		p = ar_process_strtbl(p, a, hdr);
+		goto exit;
+	}
+
+	/* check if record is symbol index */
+	ret = strncmp(hdr->ar_name, GNU_AR_SYM_TBL_MAG,
+		      strlen(GNU_AR_SYM_TBL_MAG));
+	if (!ret) {
+		p = ar_process_sym_tbl(p, a, hdr);
+		goto exit;
+	}
+
+	/* it's a file */
+	p = ar_process_file(p, a, hdr);
+
+exit:
+	return p;
+}
+
+
+/**
+ * @brief determine an archives file count
+ *
+ * @param p a pointer to the next record
+ * @param a a struct archive
+ *
+ * @return the archive file count
+ */
+
+static unsigned int ar_get_filecount(char *p, struct archive *a)
+{
+	int cnt = 0;
+	int ret;
+
+	struct ar_hdr hdr;
+
+
+	while (p < (a->ar_base + a->ar_size)) {
+		p = ar_read_hdr(p, &hdr);
+
+		p += ar_get_entry_size(&hdr);
+
+
+		/* skip string table */
+		ret = strncmp(hdr.ar_name, GNU_AR_STR_TBL_MAG,
+			      strlen(GNU_AR_STR_TBL_MAG));
+		if (!ret)
+			continue;
+
+		/* skip symbol */
+		ret = strncmp(hdr.ar_name, GNU_AR_SYM_TBL_MAG,
+			      strlen(GNU_AR_SYM_TBL_MAG));
+		if (!ret)
+			continue;
+
+
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+/**
+ * @brief print files in the archive
+ *
+ * @param a a struct archive
+ * @param name the file name to search for
+ *
+ * @return a pointer or NULL if not found
+ *
+ * @note this silently returns the first occurence only
+ */
+
+void ar_list_files(struct archive *a)
+{
+	unsigned long i;
+
+
+	if (!a)
+		return;
+
+	if (!a->fname)
+		return;
+
+	if (!a->fnamesz)
+		return;
+
+	printk("[AR]:\n"
+	       "[AR]: File contents of archive at %p\n"
+	       "[AR]:\n",
+	       a->ar_base);
+	printk("[AR]:\t[NAME]\t\t\t[SIZE]\t[ADDR]\n");
+
+	for (i = 0; i < a->n_file; i++) {
+		printk("[AR]:\t%.*s\t\t%d\t%p\n",
+		       a->fnamesz[i], a->fname[i], a->filesz[i], a->p_file[i]);
+	}
+
+	printk("[AR]:\n");
+}
+
+
+
+/**
+ * @brief return a pointer to an archive file
+ *
+ * @param a a struct archive
+ * @param name the file name to search for
+ *
+ * @return a pointer or NULL if not found
+ *
+ * @note this silently returns the first occurence only
+ */
+
+void *ar_find_file(struct archive *a, const char *name)
+{
+	unsigned long i;
+
+
+	if (!a)
+		goto error;
+
+	if (!a->fname)
+		goto error;
+
+	if (!a->fnamesz)
+		goto error;
+
+
+	for (i = 0; i < a->n_file; i++) {
+		if(!strncmp(a->fname[i], name, a->fnamesz[i]))
+			return a->p_file[i];
+	}
+
+error:
+	return NULL;
+}
+
+
+/**
+ * @brief return a pointer to the archive file a symbol is located in
+ *
+ * @param a a struct archive
+ * @param name the name to search for
+ *
+ * @return a pointer or NULL if not found
+ *
+ * @note this silently returns the first occurence only
+ */
+
+void *ar_find_symbol(struct archive *a, const char *name)
+{
+	unsigned long i;
+
+
+	if (!a)
+		return NULL;
+
+	for (i = 0; i < a->n_sym; i++) {
+		if(!strncmp(a->sym[i], name, strlen(a->sym[i])))
+			return a->p_sym[i];
+	}
+
+	return NULL;
+}
+
+
+/**
+ * @brief free memory in a struct archive
+ *
+ * @param a a struct archive
+ */
+
+void ar_free(struct archive *a)
+{
+	kfree(a->fname);
+	kfree(a->p_file);
+	kfree(a->filesz);
+	kfree(a->fnamesz);
+
+	kfree(a->sym);
+	kfree(a->p_sym);
+}
+
+
+/**
+ * @brief load/parse an ar archive
+ *
+ * @param p the memory location of the ar file
+ *
+ * @param size the size of the archive file
+ *
+ * @param a a struct archive
+ *
+ *
+ * @return 0 on success, < 0 on error
+ *
+ */
+
+int ar_load(char *p, unsigned long size, struct archive *a)
+{
+	unsigned int n;
+
+	struct ar_hdr hdr;
+
+
+	/* initialise */
+	a->ar_base = p;
+	a->ar_size = size;
+	a->ar_str  = NULL;
+	a->n_file  = 0;
+	a->n_sym   = 0;
+	a->fname   = NULL;
+	a->p_file  = NULL;
+	a->filesz  = NULL;
+	a->fnamesz = NULL;
+	a->sym     = NULL;
+	a->p_sym   = NULL;
+
+	/* verify file type */
+	p = ar_check_magic(p);
+	if (!p)
+		goto error;
+
+
+	/* don't set a->n_file, we increment that as we read the records */
+	n = ar_get_filecount(p, a);
+
+	pr_debug("[AR]: %d files in archive\n", n);
+
+	a->fname = (typeof(a->fname)) kmalloc(n * sizeof(typeof(a->fname)));
+	if (!a->fname) {
+		pr_err("[AR]: Error allocating fname name string references.\n");
+		goto error;
+	}
+
+	a->p_file = (typeof(a->p_file)) kmalloc(n * sizeof(typeof(a->p_file)));
+	if (!a->p_file) {
+		pr_err("[AR]: Error allocating file pointer references.\n");
+		goto error;
+	}
+
+	a->filesz = (typeof(a->filesz)) kmalloc(n * sizeof(typeof(a->filesz)));
+	if (!a->filesz) {
+		pr_err("[AR]: Error allocating file size references.\n");
+		goto error;
+	}
+
+	a->fnamesz = (typeof(a->fnamesz)) kmalloc(n * sizeof(typeof(a->fnamesz)));
+	if (!a->fnamesz) {
+		pr_err("[AR]: Error allocating file name size references.\n");
+		goto error;
+	}
+
+
+	while (p < (a->ar_base + a->ar_size)) {
+		/* read next header record */
+		p = ar_read_hdr(p, &hdr);
+
+		p = ar_process_hdr(p, a, &hdr);
+		if (!p)
+			goto error;
+	}
+
+	
+	return 0;
+
+error:
+	pr_err("[AR]: Error occured in ar_load()\n");
+	ar_free(a);
+
+	return -1;
+}
diff --git a/samples/ar/Makefile b/samples/ar/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4496ad5521e2faa11abf510a4d409a32f74589ea
--- /dev/null
+++ b/samples/ar/Makefile
@@ -0,0 +1,28 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-$(CONFIG_SAMPLE_AR) := ar_demo
+
+# I guess I'm too stupid to figure out the proper way to do this
+# (but maybe there is none)
+
+ifdef CROSS_COMPILE
+HOSTCC := $(CROSS_COMPILE)gcc
+HOSTLD := $(CROSS_COMPILE)ld
+endif
+
+
+HOSTCFLAGS_ar_demo.o += -I$(objtree)/include
+ar_demo-objs := ar_demo.o
+
+ifndef CROSS_COMPILE
+EXTRAPFLAG = -m32
+else
+EXTRAPFLAG =
+endif
+
+HOSTCFLAGS_ar_demo.o +=  $(EXTRAFLAG)
+HOSTLOADLIBES_ar_demo += $(EXTRAFLAG) $(objtree)/lib/lib.a
+HOSTLOADLIBES_ar_demo += $(objtree)/kernel/built-in.o
+
+always := $(hostprogs-y)
diff --git a/samples/ar/ar_demo.c b/samples/ar/ar_demo.c
new file mode 100644
index 0000000000000000000000000000000000000000..c5dce4b1db2590e4010f80435a6809f601ea0360
--- /dev/null
+++ b/samples/ar/ar_demo.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <ar.h>
+
+
+
+
+int main(int argc, char **argv)
+{
+	FILE *f;
+
+	char *buf;
+	size_t len;
+
+	struct archive a;
+	int ret;
+
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s [ar file]\n", argv[0]);
+		return 1;
+	}
+
+	f = fopen(argv[1], "rb");
+
+	if (!f) {
+		perror("opening file");
+		return 1;
+	}
+
+	fseek(f, 0, SEEK_END);
+	len = ftell(f);
+	fseek(f, 0, SEEK_SET);
+
+
+	buf = (char *) malloc(len + 1);
+
+
+	if (!buf) {
+		perror("malloc");
+                fclose(f);
+		return 1;
+	}
+
+	fread(buf, len, 1, f);
+	fclose(f);
+
+
+	archive_load(buf, len, &a);
+
+	free(buf);
+	return 0;
+}
+