[syslinux:elflink] elflink: copy some elflink related files over

syslinux-bot for Feng Tang feng.tang at intel.com
Thu Aug 12 21:03:09 PDT 2010


Commit-ID:  70aa5968a72030aff10fbdb1cb2c1c6c08de5fec
Gitweb:     http://syslinux.zytor.com/commit/70aa5968a72030aff10fbdb1cb2c1c6c08de5fec
Author:     Feng Tang <feng.tang at intel.com>
AuthorDate: Thu, 3 Jun 2010 11:00:22 +0800
Committer:  Feng Tang <feng.tang at intel.com>
CommitDate: Tue, 20 Jul 2010 11:10:03 +0800

elflink: copy some elflink related files over

also make sure the compiling passed


---
 build.sh                                           |    4 +
 com32/elflink/Makefile                             |   42 +
 com32/elflink/modules/MCONFIG                      |   69 ++
 com32/elflink/modules/Makefile                     |   90 ++
 com32/elflink/modules/README                       |    1 +
 com32/elflink/modules/ansiraw.c                    |  115 +++
 com32/elflink/modules/background.c                 |   54 ++
 .../{libutil/include => elflink/modules}/base64.h  |    0
 com32/elflink/modules/cli.h                        |   19 +
 com32/elflink/modules/crypt-md5.c                  |  173 ++++
 com32/elflink/modules/drain.c                      |   40 +
 com32/elflink/modules/get_key.c                    |  187 ++++
 com32/elflink/modules/hello.c                      |   31 +
 com32/elflink/modules/md5.c                        |  288 ++++++
 com32/{libutil/include => elflink/modules}/md5.h   |    0
 com32/elflink/modules/menu.c                       |   19 +
 com32/elflink/modules/menu.h                       |  228 +++++
 com32/elflink/modules/menumain.c                   |  975 ++++++++++++++++++++
 com32/elflink/modules/modules.dep                  |   19 +
 com32/elflink/modules/mytest.c                     |   29 +
 com32/elflink/modules/passwd.c                     |  111 +++
 com32/elflink/modules/printmsg.c                   |  115 +++
 com32/{menu => elflink/modules}/refstr.h           |    0
 com32/{libutil/include => elflink/modules}/sha1.h  |    0
 com32/elflink/modules/sha1hash.c                   |  347 +++++++
 com32/elflink/modules/sha256crypt.c                |  559 +++++++++++
 com32/elflink/modules/sha512crypt.c                |  606 ++++++++++++
 com32/elflink/modules/sort.c                       |   80 ++
 com32/elflink/modules/sort.h                       |   18 +
 com32/elflink/modules/test.c                       |  111 +++
 com32/elflink/modules/unbase64.c                   |   91 ++
 com32/elflink/test_com32.c                         |  208 +++++
 com32/elflink/test_memalign.c                      |   48 +
 com32/include/linux/list.h                         |  463 ++++++++++
 com32/include/sys/exec.h                           |  134 +++
 com32/include/sys/module.h                         |  377 ++++++++
 com32/lib/elf32.ld                                 |  184 ++++
 com32/lib/sys/module/common.c                      |  526 +++++++++++
 com32/lib/sys/module/common.h                      |   65 ++
 com32/lib/sys/module/elf_module.c                  |  512 ++++++++++
 com32/lib/sys/module/elfutils.c                    |   89 ++
 com32/lib/sys/module/elfutils.h                    |   64 ++
 com32/lib/sys/module/exec.c                        |  400 ++++++++
 com32/lib/sys/module/shallow_module.c              |  161 ++++
 44 files changed, 7652 insertions(+), 0 deletions(-)

diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..008b8e6
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,4 @@
+make -j 8
+cp extlinux/extlinux /home/feng/test/syslinux/
+cp com32/elflink/modules/*.c32 /home/feng/test/syslinux/cd/
+sudo extlinux/extlinux -i /home/feng/test/syslinux/cd/
diff --git a/com32/elflink/Makefile b/com32/elflink/Makefile
new file mode 100644
index 0000000..9e6cc4a
--- /dev/null
+++ b/com32/elflink/Makefile
@@ -0,0 +1,42 @@
+## -----------------------------------------------------------------------
+##
+##   Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
+##
+##   This program is free software; you can redistribute it and/or modify
+##   it under the terms of the GNU General Public License as published by
+##   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+##   Boston MA 02111-1307, USA; either version 2 of the License, or
+##   (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+topdir = ../..
+include ../MCONFIG
+
+MODULES	  = test_memalign.c32 # test_com32.c32
+
+TESTFILES =
+
+all: $(MODULES) $(TESTFILES)
+
+test_memalign.elf : test_memalign.o  $(LIBS) $(C_LIBS)
+	$(LD) $(LDFLAGS) -o $@ $^
+
+test_com32.elf: CFLAGS += -DELF_DEBUG
+test_com32.elf: test_com32.o ../libutil/libutil_com.a ../lib/libcom32min.a $(LIBGCC)
+	$(LD) -n $(LDFLAGS) -o $@ test_com32.o ../libutil/libutil_com.a $(LIBGCC) --whole-archive ../lib/libcom32min.a -Map test_com32.map
+
+tidy dist:
+	rm -f *.o *.lo *.a *.lst *.elf .*.d *.map
+
+clean: tidy
+	rm -f *.lss *.c32 *.lnx *.com
+
+spotless: clean
+	rm -f *~ \#*
+
+install: all
+	mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR)
+	install -m 644 $(MODULES) $(INSTALLROOT)$(AUXDIR)
+
+-include .*.d
diff --git a/com32/elflink/modules/MCONFIG b/com32/elflink/modules/MCONFIG
new file mode 100644
index 0000000..7157e1b
--- /dev/null
+++ b/com32/elflink/modules/MCONFIG
@@ -0,0 +1,69 @@
+## -*- makefile -*- -------------------------------------------------------
+##   
+##   Copyright 2008 H. Peter Anvin - All Rights Reserved
+##
+##   This program is free software; you can redistribute it and/or modify
+##   it under the terms of the GNU General Public License as published by
+##   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+##   Boston MA 02110-1301, USA; either version 2 of the License, or
+##   (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## COM32 common configurables
+##
+
+include $(topdir)/MCONFIG
+
+GCCOPT := $(call gcc_ok,-std=gnu99,)
+GCCOPT += $(call gcc_ok,-m32,)
+GCCOPT += $(call gcc_ok,-fno-stack-protector,)
+GCCOPT += $(call gcc_ok,-fwrapv,)
+GCCOPT += $(call gcc_ok,-freg-struct-return,)
+GCCOPT += -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3
+GCCOPT += $(call gcc_ok,-fno-exceptions,)
+GCCOPT += $(call gcc_ok,-fno-asynchronous-unwind-tables,)
+GCCOPT += $(call gcc_ok,-fPIE,-fPIC)
+GCCOPT += $(call gcc_ok,-falign-functions=0,-malign-functions=0)
+GCCOPT += $(call gcc_ok,-falign-jumps=0,-malign-jumps=0)
+GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
+GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
+GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
+
+com32 = $(topdir)/com32
+
+CFLAGS     = $(GCCOPT) -W -Wall -march=i386 \
+	     -fomit-frame-pointer -D__COM32__ -DDYNAMIC_MODULE \
+	     -nostdinc -iwithprefix include \
+	     -I$(com32)/libutil/include -I$(com32)/include
+SFLAGS     = $(GCCOPT) -D__COM32__ -march=i386
+LDFLAGS    = -m elf_i386 -shared --hash-style=gnu -T $(com32)/lib/elf32.ld
+LIBGCC    := $(shell $(CC) $(GCCOPT) --print-libgcc)
+
+LNXCFLAGS  = -I$(com32)/libutil/include -W -Wall -O -g -D_GNU_SOURCE
+LNXSFLAGS  = -g
+LNXLDFLAGS = -g
+
+.SUFFIXES: .lss .c .o
+
+.PRECIOUS: %.o
+%.o: %.S
+	$(CC) $(SFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.o
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.lo
+%.lo: %.S
+	$(CC) $(LNXSFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.lo
+%.lo: %.c
+	$(CC) $(LNXCFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.lnx
+%.lnx: %.lo $(LNXLIBS)
+	$(CC) $(LNXLDFLAGS) -o $@ $^
+
diff --git a/com32/elflink/modules/Makefile b/com32/elflink/modules/Makefile
new file mode 100644
index 0000000..ee16f8b
--- /dev/null
+++ b/com32/elflink/modules/Makefile
@@ -0,0 +1,90 @@
+## -----------------------------------------------------------------------
+##
+##   Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
+##
+##   This program is free software; you can redistribute it and/or modify
+##   it under the terms of the GNU General Public License as published by
+##   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+##   Boston MA 02111-1307, USA; either version 2 of the License, or
+##   (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+topdir = ../../..
+include MCONFIG
+
+MODULES	  = hello.c32 sort.c32 mytest.c32 menumain.c32 printmsg.c32 background.c32 passwd.c32 sha1hash.c32 \
+	unbase64.c32 sha512crypt.c32 md5.c32 crypt-md5.c32 sha256crypt.c32 get_key.c32 ansiraw.c32 test.c32 menu.c32 drain.c32
+
+TESTFILES =
+
+all: $(MODULES) $(TESTFILES)
+	
+test.c32 : test.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+menu.c32 : menu.o 
+	$(LD) $(LDFLAGS) -o $@ $^
+
+hello.c32 : hello.o
+	$(LD) $(LDFLAGS) -o $@ $^
+	
+sort.c32 : sort.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+mytest.c32 : mytest.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+menumain.c32 : menumain.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+printmsg.c32 : printmsg.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+background.c32 : background.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+drain.c32 : drain.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+passwd.c32 : passwd.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+sha1hash.c32 : sha1hash.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+unbase64.c32 : unbase64.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+sha512crypt.c32 : sha512crypt.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+sha256crypt.c32 : sha256crypt.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+md5.c32 : md5.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+crypt-md5.c32 : crypt-md5.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+get_key.c32 : get_key.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+ansiraw.c32 : ansiraw.o
+	$(LD) $(LDFLAGS) -o $@ $^
+
+tidy dist:
+	rm -f *.o *.lo *.a *.lst .*.d 
+
+clean: tidy
+	rm -f *.lss *.lnx *.com
+
+spotless: clean
+	rm -f *~ \#*
+
+install: all
+	mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR)
+	install -m 644 $(MODULES) $(INSTALLROOT)$(AUXDIR)
+
+-include .*.d
diff --git a/com32/elflink/modules/README b/com32/elflink/modules/README
new file mode 100644
index 0000000..090313a
--- /dev/null
+++ b/com32/elflink/modules/README
@@ -0,0 +1 @@
+This folder contains dynamically loadable ELF modules for SYSLINUX. 
\ No newline at end of file
diff --git a/com32/elflink/modules/ansiraw.c b/com32/elflink/modules/ansiraw.c
new file mode 100644
index 0000000..aca90ce
--- /dev/null
+++ b/com32/elflink/modules/ansiraw.c
@@ -0,0 +1,115 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ansiraw.c
+ *
+ * Configures the console for ANSI output in raw mode; versions
+ * for COM32 and Linux support.
+ */
+
+#ifdef __COM32__
+
+#include <stdio.h>
+#include <unistd.h>
+#include <console.h>
+#include <sys/module.h>
+
+static int ansiraw_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+void console_ansi_raw(void)
+{
+    openconsole(&dev_rawcon_r, &dev_ansiserial_w);
+}
+
+static void ansiraw_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(ansiraw_init);
+MODULE_EXIT(ansiraw_exit);
+
+#else
+
+#include <stdio.h>
+#include <termios.h>
+#include <sys/module.h>
+
+static int ansiraw_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+static struct termios original_termios_settings;
+
+static void __attribute__ ((constructor)) console_init(void)
+{
+    tcgetattr(0, &original_termios_settings);
+}
+
+static void __attribute__ ((destructor)) console_cleanup(void)
+{
+    tcsetattr(0, TCSANOW, &original_termios_settings);
+}
+
+void console_ansi_raw(void)
+{
+    struct termios tio;
+
+    /* Disable stdio buffering */
+    setbuf(stdin, NULL);
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    /* Set the termios flag so we behave the same as libcom32 */
+    tcgetattr(0, &tio);
+    tio.c_iflag &= ~ICRNL;
+    tio.c_iflag |= IGNCR;
+    tio.c_lflag &= ~(ISIG | ICANON | ECHO);
+    if (!tio.c_oflag & OPOST)
+	tio.c_oflag = 0;
+    tio.c_oflag |= OPOST | ONLCR;
+    tio.c_cc[VMIN] = 0;
+    tio.c_cc[VTIME] = 1;	/* Don't 100% busy-wait in Linux */
+    tcsetattr(0, TCSAFLUSH, &tio);
+}
+
+static void ansiraw_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(ansiraw_init);
+MODULE_EXIT(ansiraw_exit);
+
+#endif
diff --git a/com32/elflink/modules/background.c b/com32/elflink/modules/background.c
new file mode 100644
index 0000000..61d8c60
--- /dev/null
+++ b/com32/elflink/modules/background.c
@@ -0,0 +1,54 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <consoles.h>
+#include <string.h>
+#include <syslinux/vesacon.h>
+#include <sys/module.h>
+#include "menu.h"
+
+static int background_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+const char *current_background = NULL;
+
+int draw_background(const char *what)
+{
+#if 0
+    if (!what)
+	return vesacon_default_background();
+    else if (what[0] == '#')
+	return vesacon_set_background(parse_argb((char **)&what));
+    else
+	return vesacon_load_background(what);
+#endif
+}
+
+void set_background(const char *new_background)
+{
+    if (!current_background || !new_background ||
+	strcmp(current_background, new_background)) {
+	draw_background(new_background);
+	current_background = new_background;
+    }
+}
+
+static void background_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(background_init);
+MODULE_EXIT(background_exit);
diff --git a/com32/libutil/include/base64.h b/com32/elflink/modules/base64.h
similarity index 100%
copy from com32/libutil/include/base64.h
copy to com32/elflink/modules/base64.h
diff --git a/com32/elflink/modules/cli.h b/com32/elflink/modules/cli.h
new file mode 100644
index 0000000..c452643
--- /dev/null
+++ b/com32/elflink/modules/cli.h
@@ -0,0 +1,19 @@
+#ifndef CLI_H
+#define CLI_H
+
+#define MAX_CMD_HISTORY 64
+
+struct cli_command {
+    struct list_head list;
+    char *command;
+};
+
+struct list_head cli_history_head;
+
+extern void clear_screen(void);
+extern int mygetkey(clock_t timeout);
+extern const char *edit_cmdline(const char *input, int top /*, int width */ ,
+				int (*pDraw_Menu) (int, int, int),
+				void (*show_fkey) (int));
+
+#endif
diff --git a/com32/elflink/modules/crypt-md5.c b/com32/elflink/modules/crypt-md5.c
new file mode 100644
index 0000000..08e3639
--- /dev/null
+++ b/com32/elflink/modules/crypt-md5.c
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * 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.
+ */
+
+#include <inttypes.h>
+#include <md5.h>
+#include <string.h>
+#include <sys/module.h>
+
+static int crypt_md5_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+/*
+ * UNIX password
+ */
+
+static char *_crypt_to64(char *s, uint32_t v, int n)
+{
+    static const char itoa64[64] = "./0123456789"
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+    while (--n >= 0) {
+	*s++ = itoa64[v & 0x3f];
+	v >>= 6;
+    }
+    return s;
+}
+
+char *crypt_md5(const char *pw, const char *salt)
+{
+    MD5_CTX ctx, ctx1;
+    unsigned long l;
+    int sl, pl;
+    uint32_t i;
+    uint8_t final[MD5_SIZE];
+    const char *sp;
+    static char passwd[120];	/* Output buffer */
+    static const char magic[] = "$1$";
+    char *p;
+    const int magic_len = sizeof magic - 1;
+    int pwlen = strlen(pw);
+
+    /* Refine the Salt first */
+    sp = salt;
+
+    /* If it starts with the magic string, then skip that */
+    if (!strncmp(sp, magic, magic_len))
+	sp += magic_len;
+
+    /* Compute the salt length:
+       it stops at the first '$', max 8 chars */
+    for (sl = 0; sl < 8 && sp[sl] && sp[sl] != '$'; sl++) ;
+
+    MD5Init(&ctx);
+
+    /* The password first, since that is what is most unknown */
+    MD5Update(&ctx, pw, pwlen);
+
+    /* Then our magic string */
+    MD5Update(&ctx, magic, magic_len);
+
+    /* Then the raw salt */
+    MD5Update(&ctx, sp, sl);
+
+    /* Then just as many characters of the MD5(pw,salt,pw) */
+    MD5Init(&ctx1);
+    MD5Update(&ctx1, pw, pwlen);
+    MD5Update(&ctx1, sp, sl);
+    MD5Update(&ctx1, pw, pwlen);
+    MD5Final(final, &ctx1);
+    for (pl = pwlen; pl > 0; pl -= MD5_SIZE)
+	MD5Update(&ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl);
+
+    /* Don't leave anything around in vm they could use. */
+    memset(final, 0, sizeof final);
+
+    /* Then something really weird... */
+    for (i = pwlen; i; i >>= 1)
+	if (i & 1)
+	    MD5Update(&ctx, final, 1);
+	else
+	    MD5Update(&ctx, pw, 1);
+
+    /* Now make the output string */
+    p = passwd;
+
+    memcpy(p, magic, magic_len);
+    p += magic_len;
+
+    memcpy(p, sp, sl);
+    p += sl;
+
+    *p++ = '$';
+
+    MD5Final(final, &ctx);
+
+    /*
+     * and now, just to make sure things don't run too fast
+     * On a 60 Mhz Pentium this takes 34 msec, so you would
+     * need 30 seconds to build a 1000 entry dictionary...
+     */
+    for (i = 0; i < 1000; i++) {
+	MD5Init(&ctx1);
+	if (i & 1)
+	    MD5Update(&ctx1, pw, pwlen);
+	else
+	    MD5Update(&ctx1, final, MD5_SIZE);
+
+	if (i % 3)
+	    MD5Update(&ctx1, sp, sl);
+
+	if (i % 7)
+	    MD5Update(&ctx1, pw, pwlen);
+
+	if (i & 1)
+	    MD5Update(&ctx1, final, MD5_SIZE);
+	else
+	    MD5Update(&ctx1, pw, pwlen);
+	MD5Final(final, &ctx1);
+    }
+
+    l = (final[0] << 16) | (final[6] << 8) | final[12];
+    p = _crypt_to64(p, l, 4);
+    l = (final[1] << 16) | (final[7] << 8) | final[13];
+    p = _crypt_to64(p, l, 4);
+    l = (final[2] << 16) | (final[8] << 8) | final[14];
+    p = _crypt_to64(p, l, 4);
+    l = (final[3] << 16) | (final[9] << 8) | final[15];
+    p = _crypt_to64(p, l, 4);
+    l = (final[4] << 16) | (final[10] << 8) | final[5];
+    p = _crypt_to64(p, l, 4);
+    l = final[11];
+    p = _crypt_to64(p, l, 2);
+    *p = '\0';
+
+    /* Don't leave anything around in vm they could use. */
+    memset(final, 0, sizeof final);
+
+    return passwd;
+}
+
+static void crypt_md5_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(crypt_md5_init);
+MODULE_EXIT(crypt_md5_exit);
diff --git a/com32/elflink/modules/drain.c b/com32/elflink/modules/drain.c
new file mode 100644
index 0000000..032003c
--- /dev/null
+++ b/com32/elflink/modules/drain.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/cpu.h>
+#include <sys/module.h>
+
+static int drain_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+void drain_keyboard(void)
+{
+    /* Prevent "ghost typing" and keyboard buffer snooping */
+    volatile char junk;
+    int rv;
+
+    do {
+	rv = read(0, (char *)&junk, 1);
+    } while (rv > 0);
+
+    junk = 0;
+
+    cli();
+    *(volatile uint8_t *)0x419 = 0;	/* Alt-XXX keyboard area */
+    *(volatile uint16_t *)0x41a = 0x1e;	/* Keyboard buffer empty */
+    *(volatile uint16_t *)0x41c = 0x1e;
+    memset((void *)0x41e, 0, 32);	/* Clear the actual keyboard buffer */
+    sti();
+}
+
+static void drain_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(drain_init);
+MODULE_EXIT(drain_exit);
diff --git a/com32/elflink/modules/get_key.c b/com32/elflink/modules/get_key.c
new file mode 100644
index 0000000..5749b05
--- /dev/null
+++ b/com32/elflink/modules/get_key.c
@@ -0,0 +1,187 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * get_key.c
+ *
+ * Get a single key, and try to pick apart function key codes.
+ * This doesn't decode anywhere close to all possiblities, but
+ * hopefully is enough to be useful.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/times.h>
+#include <getkey.h>
+#include <libutil.h>
+#include <sys/module.h>
+
+static int getkey_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+struct keycode {
+    int code;
+    int seqlen;
+    const unsigned char *seq;
+};
+
+#define MAXLEN 8
+#define CODE(x,y) { x, (sizeof y)-1, y }
+
+static const struct keycode keycodes[] = {
+    /* First, the BIOS combined codes */
+    CODE(KEY_F1, "\0\x3B"),
+    CODE(KEY_F2, "\0\x3C"),
+    CODE(KEY_F3, "\0\x3D"),
+    CODE(KEY_F4, "\0\x3E"),
+    CODE(KEY_F5, "\0\x3F"),
+    CODE(KEY_F6, "\0\x40"),
+    CODE(KEY_F7, "\0\x41"),
+    CODE(KEY_F8, "\0\x42"),
+    CODE(KEY_F9, "\0\x43"),
+    CODE(KEY_F10, "\0\x44"),
+    CODE(KEY_F11, "\0\x85"),
+    CODE(KEY_F12, "\0\x86"),
+
+    CODE(KEY_UP, "\0\x48"),
+    CODE(KEY_DOWN, "\0\x50"),
+    CODE(KEY_LEFT, "\0\x4B"),
+    CODE(KEY_RIGHT, "\0\x4D"),
+    CODE(KEY_PGUP, "\0\x49"),
+    CODE(KEY_PGDN, "\0\x51"),
+    CODE(KEY_HOME, "\0\x47"),
+    CODE(KEY_END, "\0\x4F"),
+    CODE(KEY_INSERT, "\0\x52"),
+    CODE(KEY_DELETE, "\0\x53"),
+
+    /* Now, VT/xterm/Linux codes */
+    CODE(KEY_F1, "\033[[A"),
+    CODE(KEY_F1, "\033OP"),
+    CODE(KEY_F2, "\033[[B"),
+    CODE(KEY_F2, "\033OQ"),
+    CODE(KEY_F3, "\033[[C"),
+    CODE(KEY_F3, "\033OR"),
+    CODE(KEY_F4, "\033[[D"),
+    CODE(KEY_F4, "\033OS"),
+    CODE(KEY_F5, "\033[[E"),
+    CODE(KEY_F5, "\033[15~"),
+    CODE(KEY_F6, "\033[17~"),
+    CODE(KEY_F7, "\033[18~"),
+    CODE(KEY_F8, "\033[19~"),
+    CODE(KEY_F9, "\033[20~"),
+    CODE(KEY_F10, "\033[21~"),
+    CODE(KEY_F11, "\033[23~"),
+    CODE(KEY_F12, "\033[24~"),
+
+    CODE(KEY_UP, "\033[A"),
+    CODE(KEY_DOWN, "\033[B"),
+    CODE(KEY_LEFT, "\033[D"),
+    CODE(KEY_RIGHT, "\033[C"),
+    CODE(KEY_PGUP, "\033[5~"),
+    CODE(KEY_PGUP, "\033[V"),
+    CODE(KEY_PGDN, "\033[6~"),
+    CODE(KEY_PGDN, "\033[U"),
+    CODE(KEY_HOME, "\033[1~"),
+    CODE(KEY_HOME, "\033[H"),
+    CODE(KEY_END, "\033[4~"),
+    CODE(KEY_END, "\033[F"),
+    CODE(KEY_END, "\033OF"),
+    CODE(KEY_INSERT, "\033[2~"),
+    CODE(KEY_INSERT, "\033[@"),
+    CODE(KEY_DELETE, "\033[3~"),
+};
+
+#define NCODES ((int)(sizeof keycodes/sizeof(struct keycode)))
+
+#define KEY_TIMEOUT ((CLK_TCK+9)/10)
+
+int get_key(FILE * f, clock_t timeout)
+{
+    unsigned char buffer[MAXLEN];
+    int nc, i, rv;
+    const struct keycode *kc;
+    int another;
+    unsigned char ch;
+    clock_t start;
+
+    /* We typically start in the middle of a clock tick */
+    if (timeout)
+	timeout++;
+
+    nc = 0;
+    start = times(NULL);
+    do {
+	rv = read(fileno(f), &ch, 1);
+	if (rv == 0 || (rv == -1 && errno == EAGAIN)) {
+	    clock_t lateness = times(NULL) - start;
+	    if (nc && lateness > 1 + KEY_TIMEOUT) {
+		if (nc == 1)
+		    return buffer[0];	/* timeout in sequence */
+		else if (timeout && lateness > timeout)
+		    return KEY_NONE;
+	    } else if (!nc && timeout && lateness > timeout)
+		return KEY_NONE;	/* timeout before sequence */
+
+	    do_idle();
+
+	    another = 1;
+	    continue;
+	}
+
+	start = times(NULL);
+
+	buffer[nc++] = ch;
+
+	another = 0;
+	for (i = 0, kc = keycodes; i < NCODES; i++, kc++) {
+	    if (nc == kc->seqlen && !memcmp(buffer, kc->seq, nc))
+		return kc->code;
+	    else if (nc < kc->seqlen && !memcmp(buffer, kc->seq, nc)) {
+		another = 1;
+		break;
+	    }
+	}
+    } while (another);
+
+    /* We got an unrecognized sequence; return the first character */
+    /* We really should remember this and return subsequent characters later */
+    return buffer[0];
+}
+
+static void getkey_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(getkey_init);
+MODULE_EXIT(getkey_exit);
diff --git a/com32/elflink/modules/hello.c b/com32/elflink/modules/hello.c
new file mode 100644
index 0000000..3db2353
--- /dev/null
+++ b/com32/elflink/modules/hello.c
@@ -0,0 +1,31 @@
+/*
+ * hello.c - A simple ELF module that sorts a couple of numbers
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/module.h>
+
+#include "sort.h"
+
+#define NUM_COUNT		10
+#define MAX_NUM			100
+
+//static int hello_main(int argc, char **argv)
+int hello_main(int argc, char **argv)
+{
+    int *nums = NULL;
+    int i;
+
+    nums = malloc(NUM_COUNT * sizeof(int));
+    printf("Hello, world, from 0x%08X! malloc return %p\n", (unsigned int)&hello_main), nums;
+
+    free(nums);
+
+    return 0;
+}
+
+MODULE_MAIN(hello_main);
diff --git a/com32/elflink/modules/md5.c b/com32/elflink/modules/md5.c
new file mode 100644
index 0000000..c39bd9b
--- /dev/null
+++ b/com32/elflink/modules/md5.c
@@ -0,0 +1,288 @@
+/*
+ * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ *
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * This code is the same as the code published by RSA Inc.  It has been
+ * edited for clarity and style only.
+ */
+
+#include <string.h>
+#include <endian.h>
+#include <md5.h>
+#include <sys/module.h>
+
+static int md5_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+static void MD5Transform(uint32_t[4], const unsigned char[64]);
+
+#define Encode memcpy
+#define Decode memcpy
+
+static unsigned char PADDING[64] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+	(a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+#define GG(a, b, c, d, x, s, ac) { \
+	(a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+#define HH(a, b, c, d, x, s, ac) { \
+	(a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+#define II(a, b, c, d, x, s, ac) { \
+	(a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context. */
+
+void MD5Init(MD5_CTX * context)
+{
+    context->count[0] = context->count[1] = 0;
+
+    /* Load magic initialization constants.  */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xefcdab89;
+    context->state[2] = 0x98badcfe;
+    context->state[3] = 0x10325476;
+}
+
+/*
+ * MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+
+void MD5Update(MD5_CTX * context, const void *in, size_t inputLen)
+{
+    unsigned int i, idx, partLen;
+    const unsigned char *input = in;
+
+    /* Compute number of bytes mod 64 */
+    idx = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+    /* Update number of bits */
+    if ((context->count[0] += ((uint32_t) inputLen << 3))
+	< ((uint32_t) inputLen << 3))
+	context->count[1]++;
+    context->count[1] += ((uint32_t) inputLen >> 29);
+
+    partLen = 64 - idx;
+
+    /* Transform as many times as possible. */
+    if (inputLen >= partLen) {
+	memcpy((void *)&context->buffer[idx], (const void *)input, partLen);
+	MD5Transform(context->state, context->buffer);
+
+	for (i = partLen; i + 63 < inputLen; i += 64)
+	    MD5Transform(context->state, &input[i]);
+
+	idx = 0;
+    } else
+	i = 0;
+
+    /* Buffer remaining input */
+    memcpy((void *)&context->buffer[idx], (const void *)&input[i],
+	   inputLen - i);
+}
+
+/*
+ * MD5 padding. Adds padding followed by original length.
+ */
+
+static void MD5Pad(MD5_CTX * context)
+{
+    unsigned char bits[8];
+    unsigned int idx, padLen;
+
+    /* Save number of bits */
+    Encode(bits, context->count, 8);
+
+    /* Pad out to 56 mod 64. */
+    idx = (unsigned int)((context->count[0] >> 3) & 0x3f);
+    padLen = (idx < 56) ? (56 - idx) : (120 - idx);
+    MD5Update(context, PADDING, padLen);
+
+    /* Append length (before padding) */
+    MD5Update(context, bits, 8);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+
+void MD5Final(unsigned char digest[16], MD5_CTX * context)
+{
+    /* Do padding. */
+    MD5Pad(context);
+
+    /* Store state in digest */
+    Encode(digest, context->state, 16);
+
+    /* Zeroize sensitive information. */
+    memset((void *)context, 0, sizeof(*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block. */
+
+static void MD5Transform(state, block)
+uint32_t state[4];
+const unsigned char block[64];
+{
+    uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+    Decode(x, block, 64);
+
+    /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+    FF(a, b, c, d, x[0], S11, 0xd76aa478);	/* 1 */
+    FF(d, a, b, c, x[1], S12, 0xe8c7b756);	/* 2 */
+    FF(c, d, a, b, x[2], S13, 0x242070db);	/* 3 */
+    FF(b, c, d, a, x[3], S14, 0xc1bdceee);	/* 4 */
+    FF(a, b, c, d, x[4], S11, 0xf57c0faf);	/* 5 */
+    FF(d, a, b, c, x[5], S12, 0x4787c62a);	/* 6 */
+    FF(c, d, a, b, x[6], S13, 0xa8304613);	/* 7 */
+    FF(b, c, d, a, x[7], S14, 0xfd469501);	/* 8 */
+    FF(a, b, c, d, x[8], S11, 0x698098d8);	/* 9 */
+    FF(d, a, b, c, x[9], S12, 0x8b44f7af);	/* 10 */
+    FF(c, d, a, b, x[10], S13, 0xffff5bb1);	/* 11 */
+    FF(b, c, d, a, x[11], S14, 0x895cd7be);	/* 12 */
+    FF(a, b, c, d, x[12], S11, 0x6b901122);	/* 13 */
+    FF(d, a, b, c, x[13], S12, 0xfd987193);	/* 14 */
+    FF(c, d, a, b, x[14], S13, 0xa679438e);	/* 15 */
+    FF(b, c, d, a, x[15], S14, 0x49b40821);	/* 16 */
+
+    /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+    GG(a, b, c, d, x[1], S21, 0xf61e2562);	/* 17 */
+    GG(d, a, b, c, x[6], S22, 0xc040b340);	/* 18 */
+    GG(c, d, a, b, x[11], S23, 0x265e5a51);	/* 19 */
+    GG(b, c, d, a, x[0], S24, 0xe9b6c7aa);	/* 20 */
+    GG(a, b, c, d, x[5], S21, 0xd62f105d);	/* 21 */
+    GG(d, a, b, c, x[10], S22, 0x2441453);	/* 22 */
+    GG(c, d, a, b, x[15], S23, 0xd8a1e681);	/* 23 */
+    GG(b, c, d, a, x[4], S24, 0xe7d3fbc8);	/* 24 */
+    GG(a, b, c, d, x[9], S21, 0x21e1cde6);	/* 25 */
+    GG(d, a, b, c, x[14], S22, 0xc33707d6);	/* 26 */
+    GG(c, d, a, b, x[3], S23, 0xf4d50d87);	/* 27 */
+    GG(b, c, d, a, x[8], S24, 0x455a14ed);	/* 28 */
+    GG(a, b, c, d, x[13], S21, 0xa9e3e905);	/* 29 */
+    GG(d, a, b, c, x[2], S22, 0xfcefa3f8);	/* 30 */
+    GG(c, d, a, b, x[7], S23, 0x676f02d9);	/* 31 */
+    GG(b, c, d, a, x[12], S24, 0x8d2a4c8a);	/* 32 */
+
+    /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+    HH(a, b, c, d, x[5], S31, 0xfffa3942);	/* 33 */
+    HH(d, a, b, c, x[8], S32, 0x8771f681);	/* 34 */
+    HH(c, d, a, b, x[11], S33, 0x6d9d6122);	/* 35 */
+    HH(b, c, d, a, x[14], S34, 0xfde5380c);	/* 36 */
+    HH(a, b, c, d, x[1], S31, 0xa4beea44);	/* 37 */
+    HH(d, a, b, c, x[4], S32, 0x4bdecfa9);	/* 38 */
+    HH(c, d, a, b, x[7], S33, 0xf6bb4b60);	/* 39 */
+    HH(b, c, d, a, x[10], S34, 0xbebfbc70);	/* 40 */
+    HH(a, b, c, d, x[13], S31, 0x289b7ec6);	/* 41 */
+    HH(d, a, b, c, x[0], S32, 0xeaa127fa);	/* 42 */
+    HH(c, d, a, b, x[3], S33, 0xd4ef3085);	/* 43 */
+    HH(b, c, d, a, x[6], S34, 0x4881d05);	/* 44 */
+    HH(a, b, c, d, x[9], S31, 0xd9d4d039);	/* 45 */
+    HH(d, a, b, c, x[12], S32, 0xe6db99e5);	/* 46 */
+    HH(c, d, a, b, x[15], S33, 0x1fa27cf8);	/* 47 */
+    HH(b, c, d, a, x[2], S34, 0xc4ac5665);	/* 48 */
+
+    /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+    II(a, b, c, d, x[0], S41, 0xf4292244);	/* 49 */
+    II(d, a, b, c, x[7], S42, 0x432aff97);	/* 50 */
+    II(c, d, a, b, x[14], S43, 0xab9423a7);	/* 51 */
+    II(b, c, d, a, x[5], S44, 0xfc93a039);	/* 52 */
+    II(a, b, c, d, x[12], S41, 0x655b59c3);	/* 53 */
+    II(d, a, b, c, x[3], S42, 0x8f0ccc92);	/* 54 */
+    II(c, d, a, b, x[10], S43, 0xffeff47d);	/* 55 */
+    II(b, c, d, a, x[1], S44, 0x85845dd1);	/* 56 */
+    II(a, b, c, d, x[8], S41, 0x6fa87e4f);	/* 57 */
+    II(d, a, b, c, x[15], S42, 0xfe2ce6e0);	/* 58 */
+    II(c, d, a, b, x[6], S43, 0xa3014314);	/* 59 */
+    II(b, c, d, a, x[13], S44, 0x4e0811a1);	/* 60 */
+    II(a, b, c, d, x[4], S41, 0xf7537e82);	/* 61 */
+    II(d, a, b, c, x[11], S42, 0xbd3af235);	/* 62 */
+    II(c, d, a, b, x[2], S43, 0x2ad7d2bb);	/* 63 */
+    II(b, c, d, a, x[9], S44, 0xeb86d391);	/* 64 */
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+    /* Zeroize sensitive information. */
+    memset((void *)x, 0, sizeof(x));
+}
+
+static void md5_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(md5_init);
+MODULE_EXIT(md5_exit);
diff --git a/com32/libutil/include/md5.h b/com32/elflink/modules/md5.h
similarity index 100%
copy from com32/libutil/include/md5.h
copy to com32/elflink/modules/md5.h
diff --git a/com32/elflink/modules/menu.c b/com32/elflink/modules/menu.c
new file mode 100644
index 0000000..a4faf89
--- /dev/null
+++ b/com32/elflink/modules/menu.c
@@ -0,0 +1,19 @@
+/*
+ * menu.c -- simple program to test menu_main()
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/module.h>
+
+#include "menu.h"
+
+/*
+ * useage: menu.c32 [config file]
+ */
+static int menu(int argc, char **argv)
+{
+	menu_main(argc, argv);
+	return 0;
+}
+MODULE_MAIN(menu);
diff --git a/com32/elflink/modules/menu.h b/com32/elflink/modules/menu.h
new file mode 100644
index 0000000..5b05fe8
--- /dev/null
+++ b/com32/elflink/modules/menu.h
@@ -0,0 +1,228 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * menu.h
+ *
+ * Header file for the simple menu system
+ */
+
+#ifndef MENU_H
+#define MENU_H
+
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <colortbl.h>
+#include <stdbool.h>
+#include <setjmp.h>
+#include "refstr.h"
+
+#ifndef CLK_TCK
+# define CLK_TCK sysconf(_SC_CLK_TCK)
+#endif
+
+struct menu;
+
+/* Note: the _UNRES variants must always be immediately after their
+   "normal" versions. */
+enum menu_action {
+    MA_NONE,			/* Undefined value */
+    MA_CMD,			/* Execute a command */
+    MA_DISABLED,		/* Disabled menu entry */
+    MA_SUBMENU,			/* This is a submenu entry */
+    MA_GOTO,			/* Go to another menu */
+    MA_GOTO_UNRES,		/* Unresolved go to */
+    MA_QUIT,			/* Quit to CLI */
+    MA_EXIT,			/* Exit to higher-level menu */
+    MA_EXIT_UNRES,		/* Unresolved exit */
+};
+
+struct menu_entry {
+    struct menu *menu;		/* Parent menu */
+    const char *displayname;
+    const char *label;
+    const char *passwd;
+    char *helptext;
+    const char *cmdline;
+    struct menu *submenu;
+    struct menu_entry *next;	/* Linked list of all labels across menus */
+    int entry;			/* Entry number inside menu */
+    enum menu_action action;
+    unsigned char hotkey;
+    bool save;			/* Save this entry if selected */
+};
+
+static inline bool is_disabled(struct menu_entry *me)
+{
+    return me->action == MA_DISABLED;
+}
+
+enum kernel_type {
+    /* Meta-types for internal use */
+    KT_NONE,
+    KT_LOCALBOOT,
+
+    /* The ones we can pass off to SYSLINUX, in order */
+    KT_KERNEL,			/* Undefined type */
+    KT_LINUX,			/* Linux kernel */
+    KT_BOOT,			/* Bootstrap program */
+    KT_BSS,			/* Boot sector with patch */
+    KT_PXE,			/* PXE NBP */
+    KT_FDIMAGE,			/* Floppy disk image */
+    KT_COMBOOT,			/* COMBOOT image */
+    KT_COM32,			/* COM32 image */
+    KT_CONFIG,			/* Configuration file */
+};
+
+extern const char *const kernel_types[];
+
+/* Configurable integer parameters */
+enum parameter_number {
+    P_WIDTH,
+    P_MARGIN,
+    P_PASSWD_MARGIN,
+    P_MENU_ROWS,
+    P_TABMSG_ROW,
+    P_CMDLINE_ROW,
+    P_END_ROW,
+    P_PASSWD_ROW,
+    P_TIMEOUT_ROW,
+    P_HELPMSG_ROW,
+    P_HELPMSGEND_ROW,
+    P_HSHIFT,
+    P_VSHIFT,
+    P_HIDDEN_ROW,
+
+    NPARAMS
+};
+
+/* Configurable messages */
+enum message_number {
+    MSG_TITLE,
+    MSG_AUTOBOOT,
+    MSG_TAB,
+    MSG_NOTAB,
+    MSG_PASSPROMPT,
+
+    MSG_COUNT
+};
+
+struct messages {
+    const char *name;		/* Message configuration name */
+    const char *defmsg;		/* Default message text */
+};
+
+struct menu_parameter {
+    const char *name;
+    int value;
+};
+
+extern const struct menu_parameter mparm[NPARAMS];
+
+struct fkey_help {
+    const char *textname;
+    const char *background;
+};
+
+struct menu {
+    struct menu *next;		/* Linked list of all menus */
+    const char *label;		/* Goto label for this menu */
+    struct menu *parent;
+    struct menu_entry *parent_entry;	/* Entry for self in parent */
+
+    struct menu_entry **menu_entries;
+    struct menu_entry *menu_hotkeys[256];
+
+    const char *messages[MSG_COUNT];
+    int mparm[NPARAMS];
+
+    int nentries;
+    int nentries_space;
+    int defentry;
+    int timeout;
+
+    bool allowedit;
+    bool save;			/* MENU SAVE default for this menu */
+
+    int curentry;
+    int curtop;
+
+    const char *title;
+    const char *ontimeout;
+    const char *onerror;
+    const char *menu_master_passwd;
+    const char *menu_background;
+
+    struct color_table *color_table;
+
+    struct fkey_help fkeyhelp[12];
+};
+
+extern struct menu *root_menu, *start_menu, *hide_menu, *menu_list;
+
+/* 2048 is the current definition inside syslinux */
+#define MAX_CMDLINE_LEN	 2048
+
+/* These are global parameters regardless of which menu we're displaying */
+extern int shiftkey;
+extern int hiddenmenu;
+extern long long totaltimeout;
+
+void parse_configs(char **argv);
+extern int draw_background(const char *filename);
+
+static inline int my_isspace(char c)
+{
+    return (unsigned char)c <= ' ';
+}
+
+int my_isxdigit(char c);
+unsigned int hexval(char c);
+unsigned int hexval2(const char *p);
+uint32_t parse_argb(char **p);
+
+int menu_main(int argc, char *argv[]);
+void console_prepare(void);
+void console_cleanup(void);
+
+extern const int message_base_color, menu_color_table_size;
+int mygetkey(clock_t timeout);
+int show_message_file(const char *filename, const char *background);
+
+/* passwd.c */
+int passwd_compare(const char *passwd, const char *entry);
+
+/* colors.c */
+#define MSG_COLORS_DEF_FG	0x90ffffff
+#define MSG_COLORS_DEF_BG	0x80ffffff
+#define MSG_COLORS_DEF_SHADOW	SHADOW_NORMAL
+void set_msg_colors_global(struct color_table *tbl,
+			   unsigned int fg, unsigned int bg,
+			   enum color_table_shadow shadow);
+struct color_table *default_color_table(void);
+struct color_table *copy_color_table(const struct color_table *master);
+extern const int message_base_color;
+
+/* background.c */
+extern const char *current_background;
+void set_background(const char *new_background);
+
+/* execute.c */
+void execute(const char *cmdline, enum kernel_type type);
+
+/* drain.c */
+void drain_keyboard(void);
+
+#endif /* MENU_H */
diff --git a/com32/elflink/modules/menumain.c b/com32/elflink/modules/menumain.c
new file mode 100644
index 0000000..8628585
--- /dev/null
+++ b/com32/elflink/modules/menumain.c
@@ -0,0 +1,975 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * menumain.c
+ *
+ * Simple menu system which displays a list and allows the user to select
+ * a command line and/or edit it.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <consoles.h>
+#include <getkey.h>
+#include <minmax.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <sys/module.h>
+
+#include "menu.h"
+#include "cli.h"
+
+static jmp_buf timeout_jump;
+
+static int menumain_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+/* The symbol "cm" always refers to the current menu across this file... */
+static struct menu *cm;
+
+const struct menu_parameter mparm[NPARAMS] = {
+    [P_WIDTH] = {"width", 0},
+    [P_MARGIN] = {"margin", 10},
+    [P_PASSWD_MARGIN] = {"passwordmargin", 3},
+    [P_MENU_ROWS] = {"rows", 12},
+    [P_TABMSG_ROW] = {"tabmsgrow", 18},
+    [P_CMDLINE_ROW] = {"cmdlinerow", 18},
+    [P_END_ROW] = {"endrow", -1},
+    [P_PASSWD_ROW] = {"passwordrow", 11},
+    [P_TIMEOUT_ROW] = {"timeoutrow", 20},
+    [P_HELPMSG_ROW] = {"helpmsgrow", 22},
+    [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1},
+    [P_HSHIFT] = {"hshift", 0},
+    [P_VSHIFT] = {"vshift", 0},
+    [P_HIDDEN_ROW] = {"hiddenrow", -2},
+};
+
+/* These macros assume "cm" is a pointer to the current menu */
+#define WIDTH		(cm->mparm[P_WIDTH])
+#define MARGIN		(cm->mparm[P_MARGIN])
+#define PASSWD_MARGIN	(cm->mparm[P_PASSWD_MARGIN])
+#define MENU_ROWS	(cm->mparm[P_MENU_ROWS])
+#define TABMSG_ROW	(cm->mparm[P_TABMSG_ROW]+VSHIFT)
+#define CMDLINE_ROW	(cm->mparm[P_CMDLINE_ROW]+VSHIFT)
+#define END_ROW		(cm->mparm[P_END_ROW])
+#define PASSWD_ROW	(cm->mparm[P_PASSWD_ROW]+VSHIFT)
+#define TIMEOUT_ROW	(cm->mparm[P_TIMEOUT_ROW]+VSHIFT)
+#define HELPMSG_ROW	(cm->mparm[P_HELPMSG_ROW]+VSHIFT)
+#define HELPMSGEND_ROW	(cm->mparm[P_HELPMSGEND_ROW])
+#define HSHIFT		(cm->mparm[P_HSHIFT])
+#define VSHIFT		(cm->mparm[P_VSHIFT])
+#define HIDDEN_ROW	(cm->mparm[P_HIDDEN_ROW])
+
+static char *pad_line(const char *text, int align, int width)
+{
+    static char buffer[MAX_CMDLINE_LEN];
+    int n, p;
+
+    if (width >= (int)sizeof buffer)
+	return NULL;		/* Can't do it */
+
+    n = strlen(text);
+    if (n >= width)
+	n = width;
+
+    memset(buffer, ' ', width);
+    buffer[width] = 0;
+    p = ((width - n) * align) >> 1;
+    memcpy(buffer + p, text, n);
+
+    return buffer;
+}
+
+/* Display an entry, with possible hotkey highlight.  Assumes
+   that the current attribute is the non-hotkey one, and will
+   guarantee that as an exit condition as well. */
+static void
+display_entry(const struct menu_entry *entry, const char *attrib,
+	      const char *hotattrib, int width)
+{
+    const char *p = entry->displayname;
+    char marker;
+
+    if (!p)
+	p = "";
+
+    switch (entry->action) {
+    case MA_SUBMENU:
+	marker = '>';
+	break;
+    case MA_EXIT:
+	marker = '<';
+	break;
+    default:
+	marker = 0;
+	break;
+    }
+
+    if (marker)
+	width -= 2;
+
+    while (width) {
+	if (*p) {
+	    if (*p == '^') {
+		p++;
+		if (*p && ((unsigned char)*p & ~0x20) == entry->hotkey) {
+		    fputs(hotattrib, stdout);
+		    putchar(*p++);
+		    fputs(attrib, stdout);
+		    width--;
+		}
+	    } else {
+		putchar(*p++);
+		width--;
+	    }
+	} else {
+	    putchar(' ');
+	    width--;
+	}
+    }
+
+    if (marker) {
+	putchar(' ');
+	putchar(marker);
+    }
+}
+
+static void draw_row(int y, int sel, int top, int sbtop, int sbbot)
+{
+    int i = (y - 4 - VSHIFT) + top;
+    int dis = (i < cm->nentries) && is_disabled(cm->menu_entries[i]);
+
+    printf("\033[%d;%dH\1#1\016x\017%s ",
+	   y, MARGIN + 1 + HSHIFT,
+	   (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3");
+
+    if (i >= cm->nentries) {
+	fputs(pad_line("", 0, WIDTH - 2 * MARGIN - 4), stdout);
+    } else {
+	display_entry(cm->menu_entries[i],
+		      (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3",
+		      (i == sel) ? "\1#6" : dis ? "\2#17" : "\1#4",
+		      WIDTH - 2 * MARGIN - 4);
+    }
+
+    if (cm->nentries <= MENU_ROWS) {
+	printf(" \1#1\016x\017");
+    } else if (sbtop > 0) {
+	if (y >= sbtop && y <= sbbot)
+	    printf(" \1#7\016a\017");
+	else
+	    printf(" \1#1\016x\017");
+    } else {
+	putchar(' ');		/* Don't modify the scrollbar */
+    }
+}
+
+int show_message_file(const char *filename, const char *background)
+{
+    int rv = KEY_NONE;
+    const char *old_background = NULL;
+
+    if (background) {
+	old_background = current_background;
+	set_background(background);
+    }
+
+    if (!(rv = draw_message_file(filename)))
+	rv = mygetkey(0);	/* Wait for keypress */
+
+    if (old_background)
+	set_background(old_background);
+
+    return rv;
+}
+
+static int ask_passwd(const char *menu_entry)
+{
+    char user_passwd[WIDTH], *p;
+    int done;
+    int key;
+    int x;
+    int rv;
+
+    printf("\033[%d;%dH\2#11\016l", PASSWD_ROW, PASSWD_MARGIN + 1);
+    for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++)
+	putchar('q');
+
+    printf("k\033[%d;%dHx", PASSWD_ROW + 1, PASSWD_MARGIN + 1);
+    for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++)
+	putchar(' ');
+
+    printf("x\033[%d;%dHm", PASSWD_ROW + 2, PASSWD_MARGIN + 1);
+    for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++)
+	putchar('q');
+
+    printf("j\017\033[%d;%dH\2#12 %s \033[%d;%dH\2#13",
+	   PASSWD_ROW, (WIDTH - (strlen(cm->messages[MSG_PASSPROMPT]) + 2)) / 2,
+	   cm->messages[MSG_PASSPROMPT], PASSWD_ROW + 1, PASSWD_MARGIN + 3);
+
+    drain_keyboard();
+
+    /* Actually allow user to type a password, then compare to the SHA1 */
+    done = 0;
+    p = user_passwd;
+
+    while (!done) {
+	key = mygetkey(0);
+
+	switch (key) {
+	case KEY_ENTER:
+	case KEY_CTRL('J'):
+	    done = 1;
+	    break;
+
+	case KEY_ESC:
+	case KEY_CTRL('C'):
+	    p = user_passwd;	/* No password entered */
+	    done = 1;
+	    break;
+
+	case KEY_BACKSPACE:
+	case KEY_DEL:
+	case KEY_DELETE:
+	    if (p > user_passwd) {
+		printf("\b \b");
+		p--;
+	    }
+	    break;
+
+	case KEY_CTRL('U'):
+	    while (p > user_passwd) {
+		printf("\b \b");
+		p--;
+	    }
+	    break;
+
+	default:
+	    if (key >= ' ' && key <= 0xFF &&
+		(p - user_passwd) < WIDTH - 2 * PASSWD_MARGIN - 5) {
+		*p++ = key;
+		putchar('*');
+	    }
+	    break;
+	}
+    }
+
+    if (p == user_passwd)
+	return 0;		/* No password entered */
+
+    *p = '\0';
+
+    rv = (cm->menu_master_passwd &&
+	  passwd_compare(cm->menu_master_passwd, user_passwd))
+	|| (menu_entry && passwd_compare(menu_entry, user_passwd));
+
+    /* Clean up */
+    memset(user_passwd, 0, WIDTH);
+    drain_keyboard();
+
+    return rv;
+}
+
+static int draw_menu(int sel, int top, int edit_line)
+{
+    int x, y;
+    int sbtop = 0, sbbot = 0;
+    const char *tabmsg;
+    int tabmsg_len;
+
+    if (cm->nentries > MENU_ROWS) {
+	int sblen = max(MENU_ROWS * MENU_ROWS / cm->nentries, 1);
+	sbtop = (MENU_ROWS - sblen + 1) * top / (cm->nentries - MENU_ROWS + 1);
+	sbbot = sbtop + sblen - 1;
+	sbtop += 4;
+	sbbot += 4;		/* Starting row of scrollbar */
+    }
+
+    printf("\033[%d;%dH\1#1\016l", VSHIFT + 1, HSHIFT + MARGIN + 1);
+    for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++)
+	putchar('q');
+
+    printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x",
+	   VSHIFT + 2,
+	   HSHIFT + MARGIN + 1, pad_line(cm->title, 1, WIDTH - 2 * MARGIN - 4));
+
+    printf("\033[%d;%dH\1#1t", VSHIFT + 3, HSHIFT + MARGIN + 1);
+    for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++)
+	putchar('q');
+    fputs("u\017", stdout);
+
+    for (y = 4 + VSHIFT; y < 4 + VSHIFT + MENU_ROWS; y++)
+	draw_row(y, sel, top, sbtop, sbbot);
+
+    printf("\033[%d;%dH\1#1\016m", y, HSHIFT + MARGIN + 1);
+    for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++)
+	putchar('q');
+    fputs("j\017", stdout);
+
+    if (edit_line && cm->allowedit && !cm->menu_master_passwd)
+	tabmsg = cm->messages[MSG_TAB];
+    else
+	tabmsg = cm->messages[MSG_NOTAB];
+
+    tabmsg_len = strlen(tabmsg);
+
+    printf("\1#8\033[%d;%dH%s",
+	   TABMSG_ROW, 1 + HSHIFT + ((WIDTH - tabmsg_len) >> 1), tabmsg);
+    printf("\1#0\033[%d;1H", END_ROW);
+    return 0;
+}
+
+static void display_help(const char *text)
+{
+    int row;
+    const char *p;
+
+    if (!text) {
+	text = "";
+	printf("\1#0\033[%d;1H", HELPMSG_ROW);
+    } else {
+	printf("\2#16\033[%d;1H", HELPMSG_ROW);
+    }
+
+    for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) {
+	switch (*p) {
+	case '\r':
+	case '\f':
+	case '\v':
+	case '\033':
+	    break;
+	case '\n':
+	    printf("\033[K\033[%d;1H", ++row);
+	    break;
+	default:
+	    putchar(*p);
+	}
+    }
+
+    fputs("\033[K", stdout);
+
+    while (row <= HELPMSGEND_ROW) {
+	printf("\033[K\033[%d;1H", ++row);
+    }
+}
+
+static void show_fkey(int key)
+{
+    int fkey;
+
+    while (1) {
+	switch (key) {
+	case KEY_F1:
+	    fkey = 0;
+	    break;
+	case KEY_F2:
+	    fkey = 1;
+	    break;
+	case KEY_F3:
+	    fkey = 2;
+	    break;
+	case KEY_F4:
+	    fkey = 3;
+	    break;
+	case KEY_F5:
+	    fkey = 4;
+	    break;
+	case KEY_F6:
+	    fkey = 5;
+	    break;
+	case KEY_F7:
+	    fkey = 6;
+	    break;
+	case KEY_F8:
+	    fkey = 7;
+	    break;
+	case KEY_F9:
+	    fkey = 8;
+	    break;
+	case KEY_F10:
+	    fkey = 9;
+	    break;
+	case KEY_F11:
+	    fkey = 10;
+	    break;
+	case KEY_F12:
+	    fkey = 11;
+	    break;
+	default:
+	    fkey = -1;
+	    break;
+	}
+
+	if (fkey == -1)
+	    break;
+
+	if (cm->fkeyhelp[fkey].textname)
+	    key = show_message_file(cm->fkeyhelp[fkey].textname,
+				    cm->fkeyhelp[fkey].background);
+	else
+	    break;
+    }
+}
+
+static inline int shift_is_held(void)
+{
+    uint8_t shift_bits = *(uint8_t *) 0x417;
+
+    return !!(shift_bits & 0x5d);	/* Caps/Scroll/Alt/Shift */
+}
+
+static void print_timeout_message(int tol, int row, const char *msg)
+{
+    char buf[256];
+    int nc = 0, nnc;
+    const char *tp = msg;
+    char tc;
+    char *tq = buf;
+
+    while ((size_t) (tq - buf) < (sizeof buf - 16) && (tc = *tp)) {
+	tp++;
+	if (tc == '#') {
+	    nnc = sprintf(tq, "\2#15%d\2#14", tol);
+	    tq += nnc;
+	    nc += nnc - 8;	/* 8 formatting characters */
+	} else if (tc == '{') {
+	    /* Deal with {singular[,dual],plural} constructs */
+	    struct {
+		const char *s, *e;
+	    } tx[3];
+	    const char *tpp;
+	    int n = 0;
+
+	    memset(tx, 0, sizeof tx);
+
+	    tx[0].s = tp;
+
+	    while (*tp && *tp != '}') {
+		if (*tp == ',' && n < 2) {
+		    tx[n].e = tp;
+		    n++;
+		    tx[n].s = tp + 1;
+		}
+		tp++;
+	    }
+	    tx[n].e = tp;
+
+	    if (*tp)
+		tp++;		/* Skip final bracket */
+
+	    if (!tx[1].s)
+		tx[1] = tx[0];
+	    if (!tx[2].s)
+		tx[2] = tx[1];
+
+	    /* Now [0] is singular, [1] is dual, and [2] is plural,
+	       even if the user only specified some of them. */
+
+	    switch (tol) {
+	    case 1:
+		n = 0;
+		break;
+	    case 2:
+		n = 1;
+		break;
+	    default:
+		n = 2;
+		break;
+	    }
+
+	    for (tpp = tx[n].s; tpp < tx[n].e; tpp++) {
+		if ((size_t) (tq - buf) < (sizeof buf)) {
+		    *tq++ = *tpp;
+		    nc++;
+		}
+	    }
+	} else {
+	    *tq++ = tc;
+	    nc++;
+	}
+    }
+    *tq = '\0';
+
+    /* Let's hope 4 spaces on each side is enough... */
+    printf("\033[%d;%dH\2#14    %s    ", row,
+	   HSHIFT + 1 + ((WIDTH - nc - 8) >> 1), buf);
+}
+
+/* Set the background screen, etc. */
+static void prepare_screen_for_menu(void)
+{
+    console_color_table = cm->color_table;
+    console_color_table_size = menu_color_table_size;
+    set_background(cm->menu_background);
+}
+
+static const char *do_hidden_menu(void)
+{
+    int key;
+    int timeout_left, this_timeout;
+
+    clear_screen();
+
+    if (!setjmp(timeout_jump)) {
+	timeout_left = cm->timeout;
+
+	while (!cm->timeout || timeout_left) {
+	    int tol = timeout_left / CLK_TCK;
+
+	    print_timeout_message(tol, HIDDEN_ROW, cm->messages[MSG_AUTOBOOT]);
+
+	    this_timeout = min(timeout_left, CLK_TCK);
+	    key = mygetkey(this_timeout);
+
+	    if (key != KEY_NONE)
+		return NULL;	/* Key pressed */
+
+	    timeout_left -= this_timeout;
+	}
+    }
+
+    if (cm->ontimeout)
+	return cm->ontimeout;
+    else
+	return cm->menu_entries[cm->defentry]->cmdline;	/* Default entry */
+}
+
+static const char *run_menu(void)
+{
+    int key;
+    int done = 0;
+    volatile int entry = cm->curentry;
+    int prev_entry = -1;
+    volatile int top = cm->curtop;
+    int prev_top = -1;
+    int clear = 1, to_clear;
+    const char *cmdline = NULL;
+    volatile clock_t key_timeout, timeout_left, this_timeout;
+    const struct menu_entry *me;
+
+    /* Note: for both key_timeout and timeout == 0 means no limit */
+    timeout_left = key_timeout = cm->timeout;
+
+    /* If we're in shiftkey mode, exit immediately unless a shift key
+       is pressed */
+    if (shiftkey && !shift_is_held()) {
+	return cm->menu_entries[cm->defentry]->cmdline;
+    } else {
+	shiftkey = 0;
+    }
+
+    /* Do this before hiddenmenu handling, so we show the background */
+    prepare_screen_for_menu();
+
+    /* Handle hiddenmenu */
+    if (hiddenmenu) {
+	cmdline = do_hidden_menu();
+	if (cmdline)
+	    return cmdline;
+
+	/* Otherwise display the menu now; the timeout has already been
+	   cancelled, since the user pressed a key. */
+	hiddenmenu = 0;
+	key_timeout = 0;
+    }
+
+    /* Handle both local and global timeout */
+    if (setjmp(timeout_jump)) {
+	entry = cm->defentry;
+
+	if (top < 0 || top < entry - MENU_ROWS + 1)
+	    top = max(0, entry - MENU_ROWS + 1);
+	else if (top > entry || top > max(0, cm->nentries - MENU_ROWS))
+	    top = min(entry, max(0, cm->nentries - MENU_ROWS));
+
+	draw_menu(cm->ontimeout ? -1 : entry, top, 1);
+	cmdline =
+	    cm->ontimeout ? cm->ontimeout : cm->menu_entries[entry]->cmdline;
+	done = 1;
+    }
+
+    while (!done) {
+	if (entry <= 0) {
+	    entry = 0;
+	    while (entry < cm->nentries && is_disabled(cm->menu_entries[entry]))
+		entry++;
+	}
+	if (entry >= cm->nentries) {
+	    entry = cm->nentries - 1;
+	    while (entry > 0 && is_disabled(cm->menu_entries[entry]))
+		entry--;
+	}
+
+	me = cm->menu_entries[entry];
+
+	if (top < 0 || top < entry - MENU_ROWS + 1)
+	    top = max(0, entry - MENU_ROWS + 1);
+	else if (top > entry || top > max(0, cm->nentries - MENU_ROWS))
+	    top = min(entry, max(0, cm->nentries - MENU_ROWS));
+
+	/* Start with a clear screen */
+	if (clear) {
+	    /* Clear and redraw whole screen */
+	    /* Enable ASCII on G0 and DEC VT on G1; do it in this order
+	       to avoid confusing the Linux console */
+	    if (clear >= 2)
+		prepare_screen_for_menu();
+	    clear_screen();
+	    clear = 0;
+	    prev_entry = prev_top = -1;
+	}
+
+	if (top != prev_top) {
+	    draw_menu(entry, top, 1);
+	    display_help(me->helptext);
+	} else if (entry != prev_entry) {
+	    draw_row(prev_entry - top + 4 + VSHIFT, entry, top, 0, 0);
+	    draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0);
+	    display_help(me->helptext);
+	}
+
+	prev_entry = entry;
+	prev_top = top;
+	cm->curentry = entry;
+	cm->curtop = top;
+
+	/* Cursor movement cancels timeout */
+	if (entry != cm->defentry)
+	    key_timeout = 0;
+
+	if (key_timeout) {
+	    int tol = timeout_left / CLK_TCK;
+	    print_timeout_message(tol, TIMEOUT_ROW, cm->messages[MSG_AUTOBOOT]);
+	    to_clear = 1;
+	} else {
+	    to_clear = 0;
+	}
+
+	this_timeout = min(min(key_timeout, timeout_left), (clock_t) CLK_TCK);
+	key = mygetkey(this_timeout);
+
+	if (key != KEY_NONE) {
+	    timeout_left = key_timeout;
+	    if (to_clear)
+		printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW);
+	}
+
+	switch (key) {
+	case KEY_NONE:		/* Timeout */
+	    /* This is somewhat hacky, but this at least lets the user
+	       know what's going on, and still deals with "phantom inputs"
+	       e.g. on serial ports.
+
+	       Warning: a timeout will boot the default entry without any
+	       password! */
+	    if (key_timeout) {
+		if (timeout_left <= this_timeout)
+		    longjmp(timeout_jump, 1);
+
+		timeout_left -= this_timeout;
+	    }
+	    break;
+
+	case KEY_CTRL('L'):
+	    clear = 1;
+	    break;
+
+	case KEY_ENTER:
+	case KEY_CTRL('J'):
+	    key_timeout = 0;	/* Cancels timeout */
+	    if (me->passwd) {
+		clear = 1;
+		done = ask_passwd(me->passwd);
+	    } else {
+		done = 1;
+	    }
+	    cmdline = NULL;
+	    if (done) {
+		switch (me->action) {
+		case MA_CMD:
+		    cmdline = me->cmdline;
+		    break;
+		case MA_SUBMENU:
+		case MA_GOTO:
+		case MA_EXIT:
+		    done = 0;
+		    clear = 2;
+		    cm = me->submenu;
+		    entry = cm->curentry;
+		    top = cm->curtop;
+		    break;
+		case MA_QUIT:
+		    /* Quit menu system */
+		    done = 1;
+		    clear = 1;
+		    draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
+		    break;
+		default:
+		    done = 0;
+		    break;
+		}
+	    }
+	    if (done && !me->passwd) {
+		/* Only save a new default if we don't have a password... */
+		/*
+		if (me->save && me->label) {
+		    syslinux_setadv(ADV_MENUSAVE, strlen(me->label), me->label);
+		    syslinux_adv_write();
+		}
+		*/
+	    }
+	    break;
+
+	case KEY_UP:
+	case KEY_CTRL('P'):
+	    while (entry > 0) {
+		entry--;
+		if (entry < top)
+		    top -= MENU_ROWS;
+		if (!is_disabled(cm->menu_entries[entry]))
+		    break;
+	    }
+	    break;
+
+	case KEY_DOWN:
+	case KEY_CTRL('N'):
+	    while (entry < cm->nentries - 1) {
+		entry++;
+		if (entry >= top + MENU_ROWS)
+		    top += MENU_ROWS;
+		if (!is_disabled(cm->menu_entries[entry]))
+		    break;
+	    }
+	    break;
+
+	case KEY_PGUP:
+	case KEY_LEFT:
+	case KEY_CTRL('B'):
+	case '<':
+	    entry -= MENU_ROWS;
+	    top -= MENU_ROWS;
+	    while (entry > 0 && is_disabled(cm->menu_entries[entry])) {
+		entry--;
+		if (entry < top)
+		    top -= MENU_ROWS;
+	    }
+	    break;
+
+	case KEY_PGDN:
+	case KEY_RIGHT:
+	case KEY_CTRL('F'):
+	case '>':
+	case ' ':
+	    entry += MENU_ROWS;
+	    top += MENU_ROWS;
+	    while (entry < cm->nentries - 1
+		   && is_disabled(cm->menu_entries[entry])) {
+		entry++;
+		if (entry >= top + MENU_ROWS)
+		    top += MENU_ROWS;
+	    }
+	    break;
+
+	case '-':
+	    while (entry > 0) {
+		entry--;
+		top--;
+		if (!is_disabled(cm->menu_entries[entry]))
+		    break;
+	    }
+	    break;
+
+	case '+':
+	    while (entry < cm->nentries - 1) {
+		entry++;
+		top++;
+		if (!is_disabled(cm->menu_entries[entry]))
+		    break;
+	    }
+	    break;
+
+	case KEY_CTRL('A'):
+	case KEY_HOME:
+	    top = entry = 0;
+	    break;
+
+	case KEY_CTRL('E'):
+	case KEY_END:
+	    entry = cm->nentries - 1;
+	    top = max(0, cm->nentries - MENU_ROWS);
+	    break;
+
+	case KEY_F1:
+	case KEY_F2:
+	case KEY_F3:
+	case KEY_F4:
+	case KEY_F5:
+	case KEY_F6:
+	case KEY_F7:
+	case KEY_F8:
+	case KEY_F9:
+	case KEY_F10:
+	case KEY_F11:
+	case KEY_F12:
+	    show_fkey(key);
+	    clear = 1;
+	    break;
+
+	case KEY_TAB:
+	    if (cm->allowedit && me->action == MA_CMD) {
+		int ok = 1;
+
+		key_timeout = 0;	/* Cancels timeout */
+		draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
+
+		if (cm->menu_master_passwd) {
+		    ok = ask_passwd(NULL);
+		    clear_screen();
+		    draw_menu(-1, top, 0);
+		} else {
+		    /* Erase [Tab] message and help text */
+		    printf("\033[%d;1H\1#0\033[K", TABMSG_ROW);
+		    display_help(NULL);
+		}
+
+		if (ok) {
+		    cmdline =
+			edit_cmdline(me->cmdline, top, &draw_menu, &show_fkey);
+		    done = !!cmdline;
+		    clear = 1;	/* In case we hit [Esc] and done is null */
+		} else {
+		    draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0);
+		}
+	    }
+	    break;
+	case KEY_CTRL('C'):	/* Ctrl-C */
+	case KEY_ESC:		/* Esc */
+	    if (cm->parent) {
+		cm = cm->parent;
+		clear = 2;
+		entry = cm->curentry;
+		top = cm->curtop;
+	    } else if (cm->allowedit) {
+		done = 1;
+		clear = 1;
+		key_timeout = 0;
+
+		draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
+
+		if (cm->menu_master_passwd)
+		    done = ask_passwd(NULL);
+	    }
+	    break;
+	default:
+	    if (key > 0 && key < 0xFF) {
+		key &= ~0x20;	/* Upper case */
+		if (cm->menu_hotkeys[key]) {
+		    key_timeout = 0;
+		    entry = cm->menu_hotkeys[key]->entry;
+		    /* Should we commit at this point? */
+		}
+	    }
+	    break;
+	}
+    }
+
+    printf("\033[?25h");	/* Show cursor */
+
+    /* Return the label name so localboot and ipappend work */
+    return cmdline;
+}
+
+static void dump_menu(struct menu *menu)
+{
+	mp("will dump menu for %s:", menu->label);
+	printf("entries num: %d\n", menu->nentries);
+	printf("defentry: %d, nam = %s\n",
+		menu->defentry, menu->menu_entries[menu->defentry]->label);
+	//printf("save: %d\n", menu->save);
+	//printf("", menu->);
+	//printf("", menu->);
+	//printf("", menu->);
+}
+
+int menu_main(int argc, char *argv[])
+{
+    const char *cmdline;
+    struct menu *m;
+    int rows, cols;
+    int i;
+
+    (void)argc;
+
+    if (getscreensize(1, &rows, &cols)) {
+	/* Unknown screen size? */
+	rows = 24;
+	cols = 80;
+    }
+
+	/*
+	 * If there is a config file given, parse it
+	 * otherwise use the config already parsed
+	 */
+	if (argc == 2)
+		parse_configs(argv + 1);
+
+    /* Some postprocessing for all menus */
+    for (m = menu_list; m; m = m->next) {
+	if (!m->mparm[P_WIDTH])
+	    m->mparm[P_WIDTH] = cols;
+
+	/* If anyone has specified negative parameters, consider them
+	   relative to the bottom row of the screen. */
+	for (i = 0; i < NPARAMS; i++)
+	    if (m->mparm[i] < 0)
+		m->mparm[i] = max(m->mparm[i] + rows, 0);
+    }
+
+    cm = start_menu;
+    if (!cm->nentries) {
+	fputs("Initial menu has no LABEL entries!\n", stdout);
+	return 1;		/* Error! */
+    }
+
+    dump_menu(cm);
+    for (;;) {
+	cmdline = run_menu();
+
+	printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
+
+	if (cmdline) {
+		mp("cmdline = %s", cmdline);
+	    execute(cmdline, KT_NONE);
+	    if (cm->onerror)
+		execute(cm->onerror, KT_NONE);
+	} else {
+	    return 0;		/* Exit */
+	}
+    }
+}
+
+static void menumain_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(menumain_init);
+MODULE_EXIT(menumain_exit);
diff --git a/com32/elflink/modules/modules.dep b/com32/elflink/modules/modules.dep
new file mode 100644
index 0000000..90feb0b
--- /dev/null
+++ b/com32/elflink/modules/modules.dep
@@ -0,0 +1,19 @@
+ansiraw:
+background:
+crypt-md5: md5
+drain:
+execute:
+get_key:
+hello:
+md5:
+menu: menumain
+menumain: drain passwd background printmsg execute
+mytest: ansiraw menumain
+passwd: crypt-md5 sha1hash unbase64 sha256crypt sha512crypt
+printmsg:
+sha1hash:
+sha256crypt:
+sha512crypt:
+sort:
+test:
+unbase64:
diff --git a/com32/elflink/modules/mytest.c b/com32/elflink/modules/mytest.c
new file mode 100644
index 0000000..ddc8a0e
--- /dev/null
+++ b/com32/elflink/modules/mytest.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <minmax.h>
+#include <console.h>
+#include <consoles.h>
+#include <alloca.h>
+#include <inttypes.h>
+#include <colortbl.h>
+#include <getkey.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <syslinux/config.h>
+#include <sys/module.h>
+
+#include "menu.h"
+
+static int mytest_main(int argc, char **argv)
+{
+    console_ansi_raw();
+    //edit_cmdline("",1);
+    menu_main(argc, argv);
+    return 0;
+}
+
+MODULE_MAIN(mytest_main);
diff --git a/com32/elflink/modules/passwd.c b/com32/elflink/modules/passwd.c
new file mode 100644
index 0000000..a467a81
--- /dev/null
+++ b/com32/elflink/modules/passwd.c
@@ -0,0 +1,111 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <string.h>
+#include <xcrypt.h>
+#include <sha1.h>
+#include <base64.h>
+#include <sys/module.h>
+
+#include "menu.h"
+
+static int passwd_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+static int passwd_compare_sha1(const char *passwd, const char *entry)
+{
+    struct {
+	SHA1_CTX ctx;
+	unsigned char sha1[20], pwdsha1[20];
+    } d;
+    const char *p;
+    int rv;
+
+    SHA1Init(&d.ctx);
+
+    if ((p = strchr(passwd + 3, '$'))) {
+	SHA1Update(&d.ctx, (void *)passwd + 3, p - (passwd + 3));
+	p++;
+    } else {
+	p = passwd + 3;		/* Assume no salt */
+    }
+
+    SHA1Update(&d.ctx, (void *)entry, strlen(entry));
+    SHA1Final(d.sha1, &d.ctx);
+
+    memset(d.pwdsha1, 0, 20);
+    unbase64(d.pwdsha1, 20, p);
+
+    rv = !memcmp(d.sha1, d.pwdsha1, 20);
+
+    memset(&d, 0, sizeof d);
+    return rv;
+}
+
+static int passwd_compare_md5(const char *passwd, const char *entry)
+{
+    const char *crypted = crypt_md5(entry, passwd + 3);
+    int len = strlen(crypted);
+
+    return !strncmp(crypted, passwd, len) &&
+	(passwd[len] == '\0' || passwd[len] == '$');
+}
+
+static int passwd_compare_sha256(const char *passwd, const char *entry)
+{
+    const char *crypted = sha256_crypt(entry, passwd + 3);
+    int len = strlen(crypted);
+
+    return !strncmp(crypted, passwd, len) &&
+	(passwd[len] == '\0' || passwd[len] == '$');
+}
+
+static int passwd_compare_sha512(const char *passwd, const char *entry)
+{
+    const char *crypted = sha512_crypt(entry, passwd + 3);
+    int len = strlen(crypted);
+
+    return !strncmp(crypted, passwd, len) &&
+	(passwd[len] == '\0' || passwd[len] == '$');
+}
+
+int passwd_compare(const char *passwd, const char *entry)
+{
+    if (passwd[0] != '$' || !passwd[1] || passwd[2] != '$') {
+	/* Plaintext passwd, yuck! */
+	return !strcmp(entry, passwd);
+    } else {
+	switch (passwd[1]) {
+	case '1':
+	    return passwd_compare_md5(passwd, entry);
+	case '4':
+	    return passwd_compare_sha1(passwd, entry);
+	case '5':
+	    return passwd_compare_sha256(passwd, entry);
+	case '6':
+	    return passwd_compare_sha512(passwd, entry);
+	default:
+	    return 0;		/* Unknown encryption algorithm -> false */
+	}
+    }
+}
+
+static void passwd_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(passwd_init);
+MODULE_EXIT(passwd_exit);
diff --git a/com32/elflink/modules/printmsg.c b/com32/elflink/modules/printmsg.c
new file mode 100644
index 0000000..da9ec29
--- /dev/null
+++ b/com32/elflink/modules/printmsg.c
@@ -0,0 +1,115 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <consoles.h>
+#include <getkey.h>
+#include <minmax.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <sha1.h>
+#include <base64.h>
+#include <colortbl.h>
+#include <sys/module.h>
+#ifdef __COM32__
+#include <com32.h>
+#endif
+
+#include "menu.h"
+
+static int printmsg_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+int draw_message_file(const char *filename)
+{
+    FILE *f;
+    int ch;
+    enum msgname_state {
+	st_init,		/* Base state */
+	st_si_1,		/* <SI> digit 1 */
+	st_si_2,		/* <SI> digit 2 */
+	st_skipline,		/* Skip until NL */
+    } state = st_init;
+    int eof = 0;
+    int attr = 0;
+
+    f = fopen(filename, "r");
+    if (!f)
+	return -1;
+
+    /* Clear screen, hide cursor, default attribute */
+    printf("\033e\033%%@\033)0\033(B\3#%03d\033[?25l\033[2J\033[H",
+	   message_base_color + 0x07);
+
+    while (!eof && (ch = getc(f)) != EOF) {
+	switch (state) {
+	case st_init:
+	    switch (ch) {
+	    case '\f':
+		fputs("\033[2J\033[H", stdout);
+		break;
+	    case 15:		/* SI */
+		state = st_si_1;
+		break;
+	    case 24:
+		state = st_skipline;
+		break;
+	    case 26:
+		eof = 1;
+		break;
+	    case '\a':
+	    case '\n':
+	    case '\r':
+		putchar(ch);
+		break;
+	    default:
+		if (ch >= 32)
+		    putchar(ch);
+		break;
+	    }
+	    break;
+
+	case st_si_1:
+	    attr = hexval(ch) << 4;
+	    state = st_si_2;
+	    break;
+
+	case st_si_2:
+	    attr |= hexval(ch);
+	    printf("\3#%03d", attr + message_base_color);
+	    state = st_init;
+	    break;
+
+	case st_skipline:
+	    if (ch == '\n')
+		state = st_init;
+	    break;
+	}
+    }
+
+    fclose(f);
+    return 0;
+}
+
+static void printmsg_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(printmsg_init);
+MODULE_EXIT(printmsg_exit);
diff --git a/com32/menu/refstr.h b/com32/elflink/modules/refstr.h
similarity index 100%
copy from com32/menu/refstr.h
copy to com32/elflink/modules/refstr.h
diff --git a/com32/libutil/include/sha1.h b/com32/elflink/modules/sha1.h
similarity index 100%
copy from com32/libutil/include/sha1.h
copy to com32/elflink/modules/sha1.h
diff --git a/com32/elflink/modules/sha1hash.c b/com32/elflink/modules/sha1hash.c
new file mode 100644
index 0000000..cafdc46
--- /dev/null
+++ b/com32/elflink/modules/sha1hash.c
@@ -0,0 +1,347 @@
+/*
+SHA-1 in C
+By Steve Reid <sreid at sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown at burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+	void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+	void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it.  This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid at sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve at edmweb.com to sreid at sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz at celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 2/03
+By H. Peter Anvin <hpa at zytor.com>
+Still 100% PD
+Modified to run on any hardware with <inttypes.h> and <netinet/in.h>
+Changed the driver program
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define SHA1HANDSOFF  */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <netinet/in.h>		/* For htonl/ntohl/htons/ntohs */
+#include <sys/module.h>
+
+#include "sha1.h"
+
+static int sha1hash_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#define blk0(i) (block->l[i] = ntohl(block->l[i]))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+#ifdef VERBOSE			/* SAK */
+void SHAPrintContext(SHA1_CTX * context, char *msg)
+{
+    printf("%s (%d,%d) %x %x %x %x %x\n",
+	   msg,
+	   context->count[0], context->count[1],
+	   context->state[0],
+	   context->state[1],
+	   context->state[2], context->state[3], context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+    uint32_t a, b, c, d, e;
+    typedef union {
+	unsigned char c[64];
+	uint32_t l[16];
+    } CHAR64LONG16;
+    CHAR64LONG16 *block;
+#ifdef SHA1HANDSOFF
+    static unsigned char workspace[64];
+    block = (CHAR64LONG16 *) workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16 *) buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a, b, c, d, e, 0);
+    R0(e, a, b, c, d, 1);
+    R0(d, e, a, b, c, 2);
+    R0(c, d, e, a, b, 3);
+    R0(b, c, d, e, a, 4);
+    R0(a, b, c, d, e, 5);
+    R0(e, a, b, c, d, 6);
+    R0(d, e, a, b, c, 7);
+    R0(c, d, e, a, b, 8);
+    R0(b, c, d, e, a, 9);
+    R0(a, b, c, d, e, 10);
+    R0(e, a, b, c, d, 11);
+    R0(d, e, a, b, c, 12);
+    R0(c, d, e, a, b, 13);
+    R0(b, c, d, e, a, 14);
+    R0(a, b, c, d, e, 15);
+    R1(e, a, b, c, d, 16);
+    R1(d, e, a, b, c, 17);
+    R1(c, d, e, a, b, 18);
+    R1(b, c, d, e, a, 19);
+    R2(a, b, c, d, e, 20);
+    R2(e, a, b, c, d, 21);
+    R2(d, e, a, b, c, 22);
+    R2(c, d, e, a, b, 23);
+    R2(b, c, d, e, a, 24);
+    R2(a, b, c, d, e, 25);
+    R2(e, a, b, c, d, 26);
+    R2(d, e, a, b, c, 27);
+    R2(c, d, e, a, b, 28);
+    R2(b, c, d, e, a, 29);
+    R2(a, b, c, d, e, 30);
+    R2(e, a, b, c, d, 31);
+    R2(d, e, a, b, c, 32);
+    R2(c, d, e, a, b, 33);
+    R2(b, c, d, e, a, 34);
+    R2(a, b, c, d, e, 35);
+    R2(e, a, b, c, d, 36);
+    R2(d, e, a, b, c, 37);
+    R2(c, d, e, a, b, 38);
+    R2(b, c, d, e, a, 39);
+    R3(a, b, c, d, e, 40);
+    R3(e, a, b, c, d, 41);
+    R3(d, e, a, b, c, 42);
+    R3(c, d, e, a, b, 43);
+    R3(b, c, d, e, a, 44);
+    R3(a, b, c, d, e, 45);
+    R3(e, a, b, c, d, 46);
+    R3(d, e, a, b, c, 47);
+    R3(c, d, e, a, b, 48);
+    R3(b, c, d, e, a, 49);
+    R3(a, b, c, d, e, 50);
+    R3(e, a, b, c, d, 51);
+    R3(d, e, a, b, c, 52);
+    R3(c, d, e, a, b, 53);
+    R3(b, c, d, e, a, 54);
+    R3(a, b, c, d, e, 55);
+    R3(e, a, b, c, d, 56);
+    R3(d, e, a, b, c, 57);
+    R3(c, d, e, a, b, 58);
+    R3(b, c, d, e, a, 59);
+    R4(a, b, c, d, e, 60);
+    R4(e, a, b, c, d, 61);
+    R4(d, e, a, b, c, 62);
+    R4(c, d, e, a, b, 63);
+    R4(b, c, d, e, a, 64);
+    R4(a, b, c, d, e, 65);
+    R4(e, a, b, c, d, 66);
+    R4(d, e, a, b, c, 67);
+    R4(c, d, e, a, b, 68);
+    R4(b, c, d, e, a, 69);
+    R4(a, b, c, d, e, 70);
+    R4(e, a, b, c, d, 71);
+    R4(d, e, a, b, c, 72);
+    R4(c, d, e, a, b, 73);
+    R4(b, c, d, e, a, 74);
+    R4(a, b, c, d, e, 75);
+    R4(e, a, b, c, d, 76);
+    R4(d, e, a, b, c, 77);
+    R4(c, d, e, a, b, 78);
+    R4(b, c, d, e, a, 79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX * context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX * context, const unsigned char *data, uint32_t len)
+{				/*
+				   JHB */
+    uint32_t i, j;		/* JHB */
+
+#ifdef VERBOSE
+    SHAPrintContext(context, "before");
+#endif
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3))
+	context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+	memcpy(&context->buffer[j], data, (i = 64 - j));
+	SHA1Transform(context->state, context->buffer);
+	for (; i + 63 < len; i += 64) {
+	    SHA1Transform(context->state, &data[i]);
+	}
+	j = 0;
+    } else
+	i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+    SHAPrintContext(context, "after ");
+#endif
+}
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX * context)
+{
+    uint32_t i;			/* JHB */
+    unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+					 >> ((3 - (i & 3)) * 8)) & 255);	/* Endian independent */
+    }
+    SHA1Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+	SHA1Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1Update(context, finalcount, 8);	/* Should cause a SHA1Transform()
+					 */
+    for (i = 0; i < 20; i++) {
+	digest[i] = (unsigned char)
+	    ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+    }
+    /* Wipe variables */
+    i = 0;			/* JHB */
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(finalcount, 0, 8);	/* SWR */
+#ifdef SHA1HANDSOFF		/* make SHA1Transform overwrite it's own static vars */
+    SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+/*************************************************************/
+
+/* This is not quite the MIME base64 algorithm: it uses _ instead of /,
+   and instead of padding the output with = characters we just make the
+   output shorter. */
+char *mybase64(uint8_t digest[20])
+{
+    static const char charz[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
+    uint8_t input[21];
+    static char output[28];
+    int i, j;
+    uint8_t *p;
+    char *q;
+    uint32_t bv;
+
+    memcpy(input, digest, 20);
+    input[20] = 0;		/* Pad to multiple of 3 bytes */
+
+    p = input;
+    q = output;
+    for (i = 0; i < 7; i++) {
+	bv = (p[0] << 16) | (p[1] << 8) | p[2];
+	p += 3;
+	for (j = 0; j < 4; j++) {
+	    *q++ = charz[(bv >> 18) & 0x3f];
+	    bv <<= 6;
+	}
+    }
+    *--q = '\0';		/* The last character is not significant */
+    return output;
+}
+
+static void sha1hash_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(sha1hash_init);
+MODULE_EXIT(sha1hash_exit);
diff --git a/com32/elflink/modules/sha256crypt.c b/com32/elflink/modules/sha256crypt.c
new file mode 100644
index 0000000..61b23f2
--- /dev/null
+++ b/com32/elflink/modules/sha256crypt.c
@@ -0,0 +1,559 @@
+/* SHA256-based Unix crypt implementation.
+   Released into the Public Domain by Ulrich Drepper <drepper at redhat.com>.  */
+
+#include <alloca.h>
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <minmax.h>
+#include <sys/types.h>
+#include <sys/module.h>
+
+#include "xcrypt.h"
+
+static int sha256crypt_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+#define MIN(x,y) min(x,y)
+#define MAX(x,y) max(x,y)
+
+/* Structure to save state of computation between the single steps.  */
+struct sha256_ctx {
+    uint32_t H[8];
+
+    uint32_t total[2];
+    uint32_t buflen;
+    char buffer[128];		/* NB: always correctly aligned for uint32_t.  */
+};
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define SWAP(n) \
+    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (FIPS 180-2:5.1.1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */  };
+
+/* Constants for SHA256 from FIPS 180-2:4.2.2.  */
+static const uint32_t K[64] = {
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+static void
+sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx)
+{
+    unsigned int t;
+    const uint32_t *words = buffer;
+    size_t nwords = len / sizeof(uint32_t);
+    uint32_t a = ctx->H[0];
+    uint32_t b = ctx->H[1];
+    uint32_t c = ctx->H[2];
+    uint32_t d = ctx->H[3];
+    uint32_t e = ctx->H[4];
+    uint32_t f = ctx->H[5];
+    uint32_t g = ctx->H[6];
+    uint32_t h = ctx->H[7];
+
+    /* First increment the byte count.  FIPS 180-2 specifies the possible
+       length of the file up to 2^64 bits.  Here we only compute the
+       number of bytes.  Do a double word increment.  */
+    ctx->total[0] += len;
+    if (ctx->total[0] < len)
+	++ctx->total[1];
+
+    /* Process all bytes in the buffer with 64 bytes in each round of
+       the loop.  */
+    while (nwords > 0) {
+	uint32_t W[64];
+	uint32_t a_save = a;
+	uint32_t b_save = b;
+	uint32_t c_save = c;
+	uint32_t d_save = d;
+	uint32_t e_save = e;
+	uint32_t f_save = f;
+	uint32_t g_save = g;
+	uint32_t h_save = h;
+
+	/* Operators defined in FIPS 180-2:4.1.2.  */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22))
+#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25))
+#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3))
+#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10))
+
+	/* It is unfortunate that C does not provide an operator for
+	   cyclic rotation.  Hope the C compiler is smart enough.  */
+#define CYCLIC(w, s) ((w >> s) | (w << (32 - s)))
+
+	/* Compute the message schedule according to FIPS 180-2:6.2.2 step 2.  */
+	for (t = 0; t < 16; ++t) {
+	    W[t] = SWAP(*words);
+	    ++words;
+	}
+	for (t = 16; t < 64; ++t)
+	    W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16];
+
+	/* The actual computation according to FIPS 180-2:6.2.2 step 3.  */
+	for (t = 0; t < 64; ++t) {
+	    uint32_t T1 = h + S1(e) + Ch(e, f, g) + K[t] + W[t];
+	    uint32_t T2 = S0(a) + Maj(a, b, c);
+	    h = g;
+	    g = f;
+	    f = e;
+	    e = d + T1;
+	    d = c;
+	    c = b;
+	    b = a;
+	    a = T1 + T2;
+	}
+
+	/* Add the starting values of the context according to FIPS 180-2:6.2.2
+	   step 4.  */
+	a += a_save;
+	b += b_save;
+	c += c_save;
+	d += d_save;
+	e += e_save;
+	f += f_save;
+	g += g_save;
+	h += h_save;
+
+	/* Prepare for the next round.  */
+	nwords -= 16;
+    }
+
+    /* Put checksum in context given as argument.  */
+    ctx->H[0] = a;
+    ctx->H[1] = b;
+    ctx->H[2] = c;
+    ctx->H[3] = d;
+    ctx->H[4] = e;
+    ctx->H[5] = f;
+    ctx->H[6] = g;
+    ctx->H[7] = h;
+}
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2:5.3.2)  */
+static void sha256_init_ctx(struct sha256_ctx *ctx)
+{
+    ctx->H[0] = 0x6a09e667;
+    ctx->H[1] = 0xbb67ae85;
+    ctx->H[2] = 0x3c6ef372;
+    ctx->H[3] = 0xa54ff53a;
+    ctx->H[4] = 0x510e527f;
+    ctx->H[5] = 0x9b05688c;
+    ctx->H[6] = 0x1f83d9ab;
+    ctx->H[7] = 0x5be0cd19;
+
+    ctx->total[0] = ctx->total[1] = 0;
+    ctx->buflen = 0;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+static void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf)
+{
+    unsigned int i;
+    /* Take yet unprocessed bytes into account.  */
+    uint32_t bytes = ctx->buflen;
+    size_t pad;
+
+    /* Now count remaining bytes.  */
+    ctx->total[0] += bytes;
+    if (ctx->total[0] < bytes)
+	++ctx->total[1];
+
+    pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+    memcpy(&ctx->buffer[bytes], fillbuf, pad);
+
+    /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+    *(uint32_t *) & ctx->buffer[bytes + pad + 4] = SWAP(ctx->total[0] << 3);
+    *(uint32_t *) & ctx->buffer[bytes + pad] = SWAP((ctx->total[1] << 3) |
+						    (ctx->total[0] >> 29));
+
+    /* Process last bytes.  */
+    sha256_process_block(ctx->buffer, bytes + pad + 8, ctx);
+
+    /* Put result from CTX in first 32 bytes following RESBUF.  */
+    for (i = 0; i < 8; ++i)
+	((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
+
+    return resbuf;
+}
+
+static void
+sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx)
+{
+    /* When we already have some bits in our internal buffer concatenate
+       both inputs first.  */
+    if (ctx->buflen != 0) {
+	size_t left_over = ctx->buflen;
+	size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+	memcpy(&ctx->buffer[left_over], buffer, add);
+	ctx->buflen += add;
+
+	if (ctx->buflen > 64) {
+	    sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx);
+
+	    ctx->buflen &= 63;
+	    /* The regions in the following copy operation cannot overlap.  */
+	    memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+		   ctx->buflen);
+	}
+
+	buffer = (const char *)buffer + add;
+	len -= add;
+    }
+
+    /* Process available complete blocks.  */
+    if (len >= 64) {
+/* To check alignment gcc has an appropriate operator.  Other
+   compilers don't.  */
+#if __GNUC__ >= 2
+# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0)
+#else
+# define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0)
+#endif
+	if (UNALIGNED_P(buffer))
+	    while (len > 64) {
+		sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx);
+		buffer = (const char *)buffer + 64;
+		len -= 64;
+	} else {
+	    sha256_process_block(buffer, len & ~63, ctx);
+	    buffer = (const char *)buffer + (len & ~63);
+	    len &= 63;
+	}
+    }
+
+    /* Move remaining bytes into internal buffer.  */
+    if (len > 0) {
+	size_t left_over = ctx->buflen;
+
+	memcpy(&ctx->buffer[left_over], buffer, len);
+	left_over += len;
+	if (left_over >= 64) {
+	    sha256_process_block(ctx->buffer, 64, ctx);
+	    left_over -= 64;
+	    memcpy(ctx->buffer, &ctx->buffer[64], left_over);
+	}
+	ctx->buflen = left_over;
+    }
+}
+
+/* Define our magic string to mark salt for SHA256 "encryption"
+   replacement.  */
+static const char sha256_salt_prefix[] = "$5$";
+
+/* Prefix for optional rounds specification.  */
+static const char sha256_rounds_prefix[] = "rounds=";
+
+/* Maximum salt string length.  */
+#define SALT_LEN_MAX 16
+/* Default number of rounds if not explicitly specified.  */
+#define ROUNDS_DEFAULT 5000
+/* Minimum number of rounds.  */
+#define ROUNDS_MIN 1000
+/* Maximum number of rounds.  */
+#define ROUNDS_MAX 999999999
+
+/* Table with characters for base64 transformation.  */
+static const char b64t[64] =
+    "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static char *sha256_crypt_r(const char *key, const char *salt, char *buffer,
+			    int buflen)
+{
+    unsigned char alt_result[32]
+	__attribute__ ((__aligned__(__alignof__(uint32_t))));
+    unsigned char temp_result[32]
+	__attribute__ ((__aligned__(__alignof__(uint32_t))));
+    struct sha256_ctx ctx;
+    struct sha256_ctx alt_ctx;
+    size_t salt_len;
+    size_t key_len;
+    size_t cnt;
+    char *cp;
+    char *copied_key = NULL;
+    char *copied_salt = NULL;
+    char *p_bytes;
+    char *s_bytes;
+    /* Default number of rounds.  */
+    size_t rounds = ROUNDS_DEFAULT;
+    bool rounds_custom = false;
+
+    /* Find beginning of salt string.  The prefix should normally always
+       be present.  Just in case it is not.  */
+    if (strncmp(sha256_salt_prefix, salt, sizeof(sha256_salt_prefix) - 1) == 0)
+	/* Skip salt prefix.  */
+	salt += sizeof(sha256_salt_prefix) - 1;
+
+    if (strncmp(salt, sha256_rounds_prefix, sizeof(sha256_rounds_prefix) - 1)
+	== 0) {
+	const char *num = salt + sizeof(sha256_rounds_prefix) - 1;
+	char *endp;
+	unsigned long int srounds = strtoul(num, &endp, 10);
+	if (*endp == '$') {
+	    salt = endp + 1;
+	    rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX));
+	    rounds_custom = true;
+	}
+    }
+
+    salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX);
+    key_len = strlen(key);
+
+    if ((key - (char *)0) % __alignof__(uint32_t) != 0) {
+	char *tmp = (char *)alloca(key_len + __alignof__(uint32_t));
+	key = copied_key = memcpy(tmp + __alignof__(uint32_t)
+				  - (tmp - (char *)0) % __alignof__(uint32_t),
+				  key, key_len);
+    }
+
+    if ((salt - (char *)0) % __alignof__(uint32_t) != 0) {
+	char *tmp = (char *)alloca(salt_len + __alignof__(uint32_t));
+	salt = copied_salt = memcpy(tmp + __alignof__(uint32_t)
+				    - (tmp - (char *)0) % __alignof__(uint32_t),
+				    salt, salt_len);
+    }
+
+    /* Prepare for the real work.  */
+    sha256_init_ctx(&ctx);
+
+    /* Add the key string.  */
+    sha256_process_bytes(key, key_len, &ctx);
+
+    /* The last part is the salt string.  This must be at most 8
+       characters and it ends at the first `$' character (for
+       compatibility with existing implementations).  */
+    sha256_process_bytes(salt, salt_len, &ctx);
+
+    /* Compute alternate SHA256 sum with input KEY, SALT, and KEY.  The
+       final result will be added to the first context.  */
+    sha256_init_ctx(&alt_ctx);
+
+    /* Add key.  */
+    sha256_process_bytes(key, key_len, &alt_ctx);
+
+    /* Add salt.  */
+    sha256_process_bytes(salt, salt_len, &alt_ctx);
+
+    /* Add key again.  */
+    sha256_process_bytes(key, key_len, &alt_ctx);
+
+    /* Now get result of this (32 bytes) and add it to the other
+       context.  */
+    sha256_finish_ctx(&alt_ctx, alt_result);
+
+    /* Add for any character in the key one byte of the alternate sum.  */
+    for (cnt = key_len; cnt > 32; cnt -= 32)
+	sha256_process_bytes(alt_result, 32, &ctx);
+    sha256_process_bytes(alt_result, cnt, &ctx);
+
+    /* Take the binary representation of the length of the key and for every
+       1 add the alternate sum, for every 0 the key.  */
+    for (cnt = key_len; cnt > 0; cnt >>= 1)
+	if ((cnt & 1) != 0)
+	    sha256_process_bytes(alt_result, 32, &ctx);
+	else
+	    sha256_process_bytes(key, key_len, &ctx);
+
+    /* Create intermediate result.  */
+    sha256_finish_ctx(&ctx, alt_result);
+
+    /* Start computation of P byte sequence.  */
+    sha256_init_ctx(&alt_ctx);
+
+    /* For every character in the password add the entire password.  */
+    for (cnt = 0; cnt < key_len; ++cnt)
+	sha256_process_bytes(key, key_len, &alt_ctx);
+
+    /* Finish the digest.  */
+    sha256_finish_ctx(&alt_ctx, temp_result);
+
+    /* Create byte sequence P.  */
+    cp = p_bytes = alloca(key_len);
+    for (cnt = key_len; cnt >= 32; cnt -= 32)
+	cp = mempcpy(cp, temp_result, 32);
+    memcpy(cp, temp_result, cnt);
+
+    /* Start computation of S byte sequence.  */
+    sha256_init_ctx(&alt_ctx);
+
+    /* For every character in the password add the entire password.  */
+    for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt)
+	sha256_process_bytes(salt, salt_len, &alt_ctx);
+
+    /* Finish the digest.  */
+    sha256_finish_ctx(&alt_ctx, temp_result);
+
+    /* Create byte sequence S.  */
+    cp = s_bytes = alloca(salt_len);
+    for (cnt = salt_len; cnt >= 32; cnt -= 32)
+	cp = mempcpy(cp, temp_result, 32);
+    memcpy(cp, temp_result, cnt);
+
+    /* Repeatedly run the collected hash value through SHA256 to burn
+       CPU cycles.  */
+    for (cnt = 0; cnt < rounds; ++cnt) {
+	/* New context.  */
+	sha256_init_ctx(&ctx);
+
+	/* Add key or last result.  */
+	if ((cnt & 1) != 0)
+	    sha256_process_bytes(p_bytes, key_len, &ctx);
+	else
+	    sha256_process_bytes(alt_result, 32, &ctx);
+
+	/* Add salt for numbers not divisible by 3.  */
+	if (cnt % 3 != 0)
+	    sha256_process_bytes(s_bytes, salt_len, &ctx);
+
+	/* Add key for numbers not divisible by 7.  */
+	if (cnt % 7 != 0)
+	    sha256_process_bytes(p_bytes, key_len, &ctx);
+
+	/* Add key or last result.  */
+	if ((cnt & 1) != 0)
+	    sha256_process_bytes(alt_result, 32, &ctx);
+	else
+	    sha256_process_bytes(p_bytes, key_len, &ctx);
+
+	/* Create intermediate result.  */
+	sha256_finish_ctx(&ctx, alt_result);
+    }
+
+    /* Now we can construct the result string.  It consists of three
+       parts.  */
+    cp = stpncpy(buffer, sha256_salt_prefix, MAX(0, buflen));
+    buflen -= sizeof(sha256_salt_prefix) - 1;
+
+    if (rounds_custom) {
+	int n = snprintf(cp, MAX(0, buflen), "%s%zu$",
+			 sha256_rounds_prefix, rounds);
+	cp += n;
+	buflen -= n;
+    }
+
+    cp = stpncpy(cp, salt, MIN((size_t) MAX(0, buflen), salt_len));
+    buflen -= MIN((size_t) MAX(0, buflen), salt_len);
+
+    if (buflen > 0) {
+	*cp++ = '$';
+	--buflen;
+    }
+#define b64_from_24bit(B2, B1, B0, N)					      \
+  do {									      \
+    unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0);			      \
+    int n = (N);							      \
+    while (n-- > 0 && buflen > 0)					      \
+      {									      \
+	*cp++ = b64t[w & 0x3f];						      \
+	--buflen;							      \
+	w >>= 6;							      \
+      }									      \
+  } while (0)
+
+    b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4);
+    b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4);
+    b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4);
+    b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4);
+    b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4);
+    b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4);
+    b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4);
+    b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4);
+    b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4);
+    b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4);
+    b64_from_24bit(0, alt_result[31], alt_result[30], 3);
+    if (buflen <= 0) {
+	errno = ERANGE;
+	buffer = NULL;
+    } else
+	*cp = '\0';		/* Terminate the string.  */
+
+    /* Clear the buffer for the intermediate result so that people
+       attaching to processes or reading core dumps cannot get any
+       information.  We do it in this way to clear correct_words[]
+       inside the SHA256 implementation as well.  */
+    sha256_init_ctx(&ctx);
+    sha256_finish_ctx(&ctx, alt_result);
+    memset(temp_result, '\0', sizeof(temp_result));
+    memset(p_bytes, '\0', key_len);
+    memset(s_bytes, '\0', salt_len);
+    memset(&ctx, '\0', sizeof(ctx));
+    memset(&alt_ctx, '\0', sizeof(alt_ctx));
+    if (copied_key != NULL)
+	memset(copied_key, '\0', key_len);
+    if (copied_salt != NULL)
+	memset(copied_salt, '\0', salt_len);
+
+    return buffer;
+}
+
+/* This entry point is equivalent to the `crypt' function in Unix
+   libcs.  */
+char *sha256_crypt(const char *key, const char *salt)
+{
+    /* We don't want to have an arbitrary limit in the size of the
+       password.  We can compute an upper bound for the size of the
+       result in advance and so we can prepare the buffer we pass to
+       `sha256_crypt_r'.  */
+    static char *buffer;
+    static int buflen;
+    int needed = (sizeof(sha256_salt_prefix) - 1
+		  + sizeof(sha256_rounds_prefix) + 9 + 1
+		  + strlen(salt) + 1 + 43 + 1);
+
+    if (buflen < needed) {
+	char *new_buffer = (char *)realloc(buffer, needed);
+	if (new_buffer == NULL)
+	    return NULL;
+
+	buffer = new_buffer;
+	buflen = needed;
+    }
+
+    return sha256_crypt_r(key, salt, buffer, buflen);
+}
+
+static void sha256crypt_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(sha256crypt_init);
+MODULE_EXIT(sha256crypt_exit);
diff --git a/com32/elflink/modules/sha512crypt.c b/com32/elflink/modules/sha512crypt.c
new file mode 100644
index 0000000..617423c
--- /dev/null
+++ b/com32/elflink/modules/sha512crypt.c
@@ -0,0 +1,606 @@
+/* SHA512-based Unix crypt implementation.
+   Released into the Public Domain by Ulrich Drepper <drepper at redhat.com>.  */
+
+#include <alloca.h>
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <minmax.h>
+#include <sys/types.h>
+#include <sys/module.h>
+
+#include "xcrypt.h"
+
+static int sha512crypt_init()
+{
+    return 0;			// Nothing to do; return success
+}
+
+#define MIN(x,y) min(x,y)
+#define MAX(x,y) max(x,y)
+
+/* Structure to save state of computation between the single steps.  */
+struct sha512_ctx {
+    uint64_t H[8];
+
+    uint64_t total[2];
+    uint64_t buflen;
+    char buffer[256];		/* NB: always correctly aligned for uint64_t.  */
+};
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define SWAP(n) \
+  (((n) << 56)					\
+   | (((n) & 0xff00) << 40)			\
+   | (((n) & 0xff0000) << 24)			\
+   | (((n) & 0xff000000) << 8)			\
+   | (((n) >> 8) & 0xff000000)			\
+   | (((n) >> 24) & 0xff0000)			\
+   | (((n) >> 40) & 0xff00)			\
+   | ((n) >> 56))
+#else
+# define SWAP(n) (n)
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (FIPS 180-2:5.1.2)  */
+static const unsigned char fillbuf[128] = { 0x80, 0 /* , 0, 0, ...  */  };
+
+/* Constants for SHA512 from FIPS 180-2:4.2.3.  */
+static const uint64_t K[80] = {
+    UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
+    UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
+    UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
+    UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
+    UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
+    UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
+    UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
+    UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
+    UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
+    UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
+    UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
+    UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
+    UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
+    UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
+    UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
+    UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
+    UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
+    UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
+    UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
+    UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
+    UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
+    UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
+    UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
+    UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
+    UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
+    UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
+    UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
+    UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
+    UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
+    UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
+    UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
+    UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
+    UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
+    UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
+    UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
+    UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
+    UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
+    UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
+    UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
+    UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
+};
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 128 == 0.  */
+static void
+sha512_process_block(const void *buffer, size_t len, struct sha512_ctx *ctx)
+{
+    unsigned int t;
+    const uint64_t *words = buffer;
+    size_t nwords = len / sizeof(uint64_t);
+    uint64_t a = ctx->H[0];
+    uint64_t b = ctx->H[1];
+    uint64_t c = ctx->H[2];
+    uint64_t d = ctx->H[3];
+    uint64_t e = ctx->H[4];
+    uint64_t f = ctx->H[5];
+    uint64_t g = ctx->H[6];
+    uint64_t h = ctx->H[7];
+
+    /* First increment the byte count.  FIPS 180-2 specifies the possible
+       length of the file up to 2^128 bits.  Here we only compute the
+       number of bytes.  Do a double word increment.  */
+    ctx->total[0] += len;
+    if (ctx->total[0] < len)
+	++ctx->total[1];
+
+    /* Process all bytes in the buffer with 128 bytes in each round of
+       the loop.  */
+    while (nwords > 0) {
+	uint64_t W[80];
+	uint64_t a_save = a;
+	uint64_t b_save = b;
+	uint64_t c_save = c;
+	uint64_t d_save = d;
+	uint64_t e_save = e;
+	uint64_t f_save = f;
+	uint64_t g_save = g;
+	uint64_t h_save = h;
+
+	/* Operators defined in FIPS 180-2:4.1.2.  */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (CYCLIC (x, 28) ^ CYCLIC (x, 34) ^ CYCLIC (x, 39))
+#define S1(x) (CYCLIC (x, 14) ^ CYCLIC (x, 18) ^ CYCLIC (x, 41))
+#define R0(x) (CYCLIC (x, 1) ^ CYCLIC (x, 8) ^ (x >> 7))
+#define R1(x) (CYCLIC (x, 19) ^ CYCLIC (x, 61) ^ (x >> 6))
+
+	/* It is unfortunate that C does not provide an operator for
+	   cyclic rotation.  Hope the C compiler is smart enough.  */
+#define CYCLIC(w, s) ((w >> s) | (w << (64 - s)))
+
+	/* Compute the message schedule according to FIPS 180-2:6.3.2 step 2.  */
+	for (t = 0; t < 16; ++t) {
+	    W[t] = SWAP(*words);
+	    ++words;
+	}
+	for (t = 16; t < 80; ++t)
+	    W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16];
+
+	/* The actual computation according to FIPS 180-2:6.3.2 step 3.  */
+	for (t = 0; t < 80; ++t) {
+	    uint64_t T1 = h + S1(e) + Ch(e, f, g) + K[t] + W[t];
+	    uint64_t T2 = S0(a) + Maj(a, b, c);
+	    h = g;
+	    g = f;
+	    f = e;
+	    e = d + T1;
+	    d = c;
+	    c = b;
+	    b = a;
+	    a = T1 + T2;
+	}
+
+	/* Add the starting values of the context according to FIPS 180-2:6.3.2
+	   step 4.  */
+	a += a_save;
+	b += b_save;
+	c += c_save;
+	d += d_save;
+	e += e_save;
+	f += f_save;
+	g += g_save;
+	h += h_save;
+
+	/* Prepare for the next round.  */
+	nwords -= 16;
+    }
+
+    /* Put checksum in context given as argument.  */
+    ctx->H[0] = a;
+    ctx->H[1] = b;
+    ctx->H[2] = c;
+    ctx->H[3] = d;
+    ctx->H[4] = e;
+    ctx->H[5] = f;
+    ctx->H[6] = g;
+    ctx->H[7] = h;
+}
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2:5.3.3)  */
+static void sha512_init_ctx(struct sha512_ctx *ctx)
+{
+    ctx->H[0] = UINT64_C(0x6a09e667f3bcc908);
+    ctx->H[1] = UINT64_C(0xbb67ae8584caa73b);
+    ctx->H[2] = UINT64_C(0x3c6ef372fe94f82b);
+    ctx->H[3] = UINT64_C(0xa54ff53a5f1d36f1);
+    ctx->H[4] = UINT64_C(0x510e527fade682d1);
+    ctx->H[5] = UINT64_C(0x9b05688c2b3e6c1f);
+    ctx->H[6] = UINT64_C(0x1f83d9abfb41bd6b);
+    ctx->H[7] = UINT64_C(0x5be0cd19137e2179);
+
+    ctx->total[0] = ctx->total[1] = 0;
+    ctx->buflen = 0;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+static void *sha512_finish_ctx(struct sha512_ctx *ctx, void *resbuf)
+{
+    unsigned int i;
+    /* Take yet unprocessed bytes into account.  */
+    uint64_t bytes = ctx->buflen;
+    size_t pad;
+
+    /* Now count remaining bytes.  */
+    ctx->total[0] += bytes;
+    if (ctx->total[0] < bytes)
+	++ctx->total[1];
+
+    pad = bytes >= 112 ? 128 + 112 - bytes : 112 - bytes;
+    memcpy(&ctx->buffer[bytes], fillbuf, pad);
+
+    /* Put the 128-bit file length in *bits* at the end of the buffer.  */
+    *(uint64_t *) & ctx->buffer[bytes + pad + 8] = SWAP(ctx->total[0] << 3);
+    *(uint64_t *) & ctx->buffer[bytes + pad] = SWAP((ctx->total[1] << 3) |
+						    (ctx->total[0] >> 61));
+
+    /* Process last bytes.  */
+    sha512_process_block(ctx->buffer, bytes + pad + 16, ctx);
+
+    /* Put result from CTX in first 64 bytes following RESBUF.  */
+    for (i = 0; i < 8; ++i)
+	((uint64_t *) resbuf)[i] = SWAP(ctx->H[i]);
+
+    return resbuf;
+}
+
+static void
+sha512_process_bytes(const void *buffer, size_t len, struct sha512_ctx *ctx)
+{
+    /* When we already have some bits in our internal buffer concatenate
+       both inputs first.  */
+    if (ctx->buflen != 0) {
+	size_t left_over = ctx->buflen;
+	size_t add = 256 - left_over > len ? len : 256 - left_over;
+
+	memcpy(&ctx->buffer[left_over], buffer, add);
+	ctx->buflen += add;
+
+	if (ctx->buflen > 128) {
+	    sha512_process_block(ctx->buffer, ctx->buflen & ~127, ctx);
+
+	    ctx->buflen &= 127;
+	    /* The regions in the following copy operation cannot overlap.  */
+	    memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~127],
+		   ctx->buflen);
+	}
+
+	buffer = (const char *)buffer + add;
+	len -= add;
+    }
+
+    /* Process available complete blocks.  */
+    if (len >= 128) {
+#if !_STRING_ARCH_unaligned
+/* To check alignment gcc has an appropriate operator.  Other
+   compilers don't.  */
+# if __GNUC__ >= 2
+#  define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint64_t) != 0)
+# else
+#  define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint64_t) != 0)
+# endif
+	if (UNALIGNED_P(buffer))
+	    while (len > 128) {
+		sha512_process_block(memcpy(ctx->buffer, buffer, 128), 128,
+				     ctx);
+		buffer = (const char *)buffer + 128;
+		len -= 128;
+	} else
+#endif
+	{
+	    sha512_process_block(buffer, len & ~127, ctx);
+	    buffer = (const char *)buffer + (len & ~127);
+	    len &= 127;
+	}
+    }
+
+    /* Move remaining bytes into internal buffer.  */
+    if (len > 0) {
+	size_t left_over = ctx->buflen;
+
+	memcpy(&ctx->buffer[left_over], buffer, len);
+	left_over += len;
+	if (left_over >= 128) {
+	    sha512_process_block(ctx->buffer, 128, ctx);
+	    left_over -= 128;
+	    memcpy(ctx->buffer, &ctx->buffer[128], left_over);
+	}
+	ctx->buflen = left_over;
+    }
+}
+
+/* Define our magic string to mark salt for SHA512 "encryption"
+   replacement.  */
+static const char sha512_salt_prefix[] = "$6$";
+
+/* Prefix for optional rounds specification.  */
+static const char sha512_rounds_prefix[] = "rounds=";
+
+/* Maximum salt string length.  */
+#define SALT_LEN_MAX 16
+/* Default number of rounds if not explicitly specified.  */
+#define ROUNDS_DEFAULT 5000
+/* Minimum number of rounds.  */
+#define ROUNDS_MIN 1000
+/* Maximum number of rounds.  */
+#define ROUNDS_MAX 999999999
+
+/* Table with characters for base64 transformation.  */
+static const char b64t[64] =
+    "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static char *sha512_crypt_r(const char *key, const char *salt, char *buffer,
+			    int buflen)
+{
+    unsigned char alt_result[64]
+	__attribute__ ((__aligned__(__alignof__(uint64_t))));
+    unsigned char temp_result[64]
+	__attribute__ ((__aligned__(__alignof__(uint64_t))));
+    struct sha512_ctx ctx;
+    struct sha512_ctx alt_ctx;
+    size_t salt_len;
+    size_t key_len;
+    size_t cnt;
+    char *cp;
+    char *copied_key = NULL;
+    char *copied_salt = NULL;
+    char *p_bytes;
+    char *s_bytes;
+    /* Default number of rounds.  */
+    size_t rounds = ROUNDS_DEFAULT;
+    bool rounds_custom = false;
+
+    /* Find beginning of salt string.  The prefix should normally always
+       be present.  Just in case it is not.  */
+    if (strncmp(sha512_salt_prefix, salt, sizeof(sha512_salt_prefix) - 1) == 0)
+	/* Skip salt prefix.  */
+	salt += sizeof(sha512_salt_prefix) - 1;
+
+    if (strncmp(salt, sha512_rounds_prefix, sizeof(sha512_rounds_prefix) - 1)
+	== 0) {
+	const char *num = salt + sizeof(sha512_rounds_prefix) - 1;
+	char *endp;
+	unsigned long int srounds = strtoul(num, &endp, 10);
+	if (*endp == '$') {
+	    salt = endp + 1;
+	    rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX));
+	    rounds_custom = true;
+	}
+    }
+
+    salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX);
+    key_len = strlen(key);
+
+    if ((key - (char *)0) % __alignof__(uint64_t) != 0) {
+	char *tmp = (char *)alloca(key_len + __alignof__(uint64_t));
+	key = copied_key = memcpy(tmp + __alignof__(uint64_t)
+				  - (tmp - (char *)0) % __alignof__(uint64_t),
+				  key, key_len);
+    }
+
+    if ((salt - (char *)0) % __alignof__(uint64_t) != 0) {
+	char *tmp = (char *)alloca(salt_len + __alignof__(uint64_t));
+	salt = copied_salt = memcpy(tmp + __alignof__(uint64_t)
+				    - (tmp - (char *)0) % __alignof__(uint64_t),
+				    salt, salt_len);
+    }
+
+    /* Prepare for the real work.  */
+    sha512_init_ctx(&ctx);
+
+    /* Add the key string.  */
+    sha512_process_bytes(key, key_len, &ctx);
+
+    /* The last part is the salt string.  This must be at most 8
+       characters and it ends at the first `$' character (for
+       compatibility with existing implementations).  */
+    sha512_process_bytes(salt, salt_len, &ctx);
+
+    /* Compute alternate SHA512 sum with input KEY, SALT, and KEY.  The
+       final result will be added to the first context.  */
+    sha512_init_ctx(&alt_ctx);
+
+    /* Add key.  */
+    sha512_process_bytes(key, key_len, &alt_ctx);
+
+    /* Add salt.  */
+    sha512_process_bytes(salt, salt_len, &alt_ctx);
+
+    /* Add key again.  */
+    sha512_process_bytes(key, key_len, &alt_ctx);
+
+    /* Now get result of this (64 bytes) and add it to the other
+       context.  */
+    sha512_finish_ctx(&alt_ctx, alt_result);
+
+    /* Add for any character in the key one byte of the alternate sum.  */
+    for (cnt = key_len; cnt > 64; cnt -= 64)
+	sha512_process_bytes(alt_result, 64, &ctx);
+    sha512_process_bytes(alt_result, cnt, &ctx);
+
+    /* Take the binary representation of the length of the key and for every
+       1 add the alternate sum, for every 0 the key.  */
+    for (cnt = key_len; cnt > 0; cnt >>= 1)
+	if ((cnt & 1) != 0)
+	    sha512_process_bytes(alt_result, 64, &ctx);
+	else
+	    sha512_process_bytes(key, key_len, &ctx);
+
+    /* Create intermediate result.  */
+    sha512_finish_ctx(&ctx, alt_result);
+
+    /* Start computation of P byte sequence.  */
+    sha512_init_ctx(&alt_ctx);
+
+    /* For every character in the password add the entire password.  */
+    for (cnt = 0; cnt < key_len; ++cnt)
+	sha512_process_bytes(key, key_len, &alt_ctx);
+
+    /* Finish the digest.  */
+    sha512_finish_ctx(&alt_ctx, temp_result);
+
+    /* Create byte sequence P.  */
+    cp = p_bytes = alloca(key_len);
+    for (cnt = key_len; cnt >= 64; cnt -= 64)
+	cp = mempcpy(cp, temp_result, 64);
+    memcpy(cp, temp_result, cnt);
+
+    /* Start computation of S byte sequence.  */
+    sha512_init_ctx(&alt_ctx);
+
+    /* For every character in the password add the entire password.  */
+    for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt)
+	sha512_process_bytes(salt, salt_len, &alt_ctx);
+
+    /* Finish the digest.  */
+    sha512_finish_ctx(&alt_ctx, temp_result);
+
+    /* Create byte sequence S.  */
+    cp = s_bytes = alloca(salt_len);
+    for (cnt = salt_len; cnt >= 64; cnt -= 64)
+	cp = mempcpy(cp, temp_result, 64);
+    memcpy(cp, temp_result, cnt);
+
+    /* Repeatedly run the collected hash value through SHA512 to burn
+       CPU cycles.  */
+    for (cnt = 0; cnt < rounds; ++cnt) {
+	/* New context.  */
+	sha512_init_ctx(&ctx);
+
+	/* Add key or last result.  */
+	if ((cnt & 1) != 0)
+	    sha512_process_bytes(p_bytes, key_len, &ctx);
+	else
+	    sha512_process_bytes(alt_result, 64, &ctx);
+
+	/* Add salt for numbers not divisible by 3.  */
+	if (cnt % 3 != 0)
+	    sha512_process_bytes(s_bytes, salt_len, &ctx);
+
+	/* Add key for numbers not divisible by 7.  */
+	if (cnt % 7 != 0)
+	    sha512_process_bytes(p_bytes, key_len, &ctx);
+
+	/* Add key or last result.  */
+	if ((cnt & 1) != 0)
+	    sha512_process_bytes(alt_result, 64, &ctx);
+	else
+	    sha512_process_bytes(p_bytes, key_len, &ctx);
+
+	/* Create intermediate result.  */
+	sha512_finish_ctx(&ctx, alt_result);
+    }
+
+    /* Now we can construct the result string.  It consists of three
+       parts.  */
+    cp = stpncpy(buffer, sha512_salt_prefix, MAX(0, buflen));
+    buflen -= sizeof(sha512_salt_prefix) - 1;
+
+    if (rounds_custom) {
+	int n = snprintf(cp, MAX(0, buflen), "%s%zu$",
+			 sha512_rounds_prefix, rounds);
+	cp += n;
+	buflen -= n;
+    }
+
+    cp = stpncpy(cp, salt, MIN((size_t) MAX(0, buflen), salt_len));
+    buflen -= MIN((size_t) MAX(0, buflen), salt_len);
+
+    if (buflen > 0) {
+	*cp++ = '$';
+	--buflen;
+    }
+#define b64_from_24bit(B2, B1, B0, N)					      \
+  do {									      \
+    unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0);			      \
+    int n = (N);							      \
+    while (n-- > 0 && buflen > 0)					      \
+      {									      \
+	*cp++ = b64t[w & 0x3f];						      \
+	--buflen;							      \
+	w >>= 6;							      \
+      }									      \
+  } while (0)
+
+    b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4);
+    b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4);
+    b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4);
+    b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4);
+    b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4);
+    b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4);
+    b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4);
+    b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4);
+    b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4);
+    b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4);
+    b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4);
+    b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4);
+    b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4);
+    b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4);
+    b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4);
+    b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4);
+    b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4);
+    b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4);
+    b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4);
+    b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4);
+    b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4);
+    b64_from_24bit(0, 0, alt_result[63], 2);
+
+    if (buflen <= 0) {
+	errno = ERANGE;
+	buffer = NULL;
+    } else
+	*cp = '\0';		/* Terminate the string.  */
+
+    /* Clear the buffer for the intermediate result so that people
+       attaching to processes or reading core dumps cannot get any
+       information.  We do it in this way to clear correct_words[]
+       inside the SHA512 implementation as well.  */
+    sha512_init_ctx(&ctx);
+    sha512_finish_ctx(&ctx, alt_result);
+    memset(temp_result, '\0', sizeof(temp_result));
+    memset(p_bytes, '\0', key_len);
+    memset(s_bytes, '\0', salt_len);
+    memset(&ctx, '\0', sizeof(ctx));
+    memset(&alt_ctx, '\0', sizeof(alt_ctx));
+    if (copied_key != NULL)
+	memset(copied_key, '\0', key_len);
+    if (copied_salt != NULL)
+	memset(copied_salt, '\0', salt_len);
+
+    return buffer;
+}
+
+/* This entry point is equivalent to the `crypt' function in Unix
+   libcs.  */
+char *sha512_crypt(const char *key, const char *salt)
+{
+    /* We don't want to have an arbitrary limit in the size of the
+       password.  We can compute an upper bound for the size of the
+       result in advance and so we can prepare the buffer we pass to
+       `sha512_crypt_r'.  */
+    static char *buffer;
+    static int buflen;
+    int needed = (sizeof(sha512_salt_prefix) - 1
+		  + sizeof(sha512_rounds_prefix) + 9 + 1
+		  + strlen(salt) + 1 + 86 + 1);
+
+    if (buflen < needed) {
+	char *new_buffer = (char *)realloc(buffer, needed);
+	if (new_buffer == NULL)
+	    return NULL;
+
+	buffer = new_buffer;
+	buflen = needed;
+    }
+
+    return sha512_crypt_r(key, salt, buffer, buflen);
+}
+
+static void sha512crypt_exit()
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(sha512crypt_init);
+MODULE_EXIT(sha512crypt_exit);
diff --git a/com32/elflink/modules/sort.c b/com32/elflink/modules/sort.c
new file mode 100644
index 0000000..f91cb60
--- /dev/null
+++ b/com32/elflink/modules/sort.c
@@ -0,0 +1,80 @@
+/*
+ * sort.c - Sample ELF module providing a quick sort function
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#include <stdlib.h>
+#include <sys/module.h>
+
+/**
+ * sort_init - Module entry point.
+ */
+static int sort_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+static inline void swap(int *x, int *y)
+{
+    int tmp;
+    tmp = *x;
+    *x = *y;
+    *y = tmp;
+}
+
+static inline int randint(int l, int u)
+{
+    return l + (rand() % (u - l + 1));
+}
+
+/**
+ * quick_sort_range - A small and efficient version of quicksort.
+ * @nums: The numbers to sort
+ * @l: The lower index in the vector (inclusive)
+ * @u: The upper index in the vector (inclusive)
+ *
+ * The implementation is taken from "Beautiful Code", by O'Reilly, the
+ * book students received from Google as a gift for their acceptance
+ * in the GSoC 2008 program. The code belongs to Jon Bentley, who
+ * wrote the third chapter of the book. Since ELF modules were written
+ * as part of this program, the author of the module considered
+ * the book had to be put to some use. :)
+ */
+static void quick_sort_range(int *nums, int l, int u)
+{
+    int i, m;
+    if (l >= u)
+	return;
+
+    swap(&nums[l], &nums[randint(l, u)]);
+
+    m = l;
+    for (i = l + 1; i <= u; i++) {
+	if (nums[i] < nums[l])
+	    swap(&nums[++m], &nums[i]);
+    }
+
+    swap(&nums[l], &nums[m]);
+
+    quick_sort_range(nums, l, m - 1);
+    quick_sort_range(nums, m + 1, u);
+}
+
+void quick_sort(int *nums, int count)
+{
+    quick_sort_range(nums, 0, count - 1);
+}
+
+/**
+ * sort_exit - Module exit point.
+ */
+static void sort_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(sort_init);
+MODULE_EXIT(sort_exit);
diff --git a/com32/elflink/modules/sort.h b/com32/elflink/modules/sort.h
new file mode 100644
index 0000000..0b49548
--- /dev/null
+++ b/com32/elflink/modules/sort.h
@@ -0,0 +1,18 @@
+/*
+ * sort.h - Quick sort module API definitions
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#ifndef SORT_H_
+#define SORT_H_
+
+/**
+ * quick_sort - In place sort of an array of numbers.
+ * @nums: Pointer to the array
+ * @count: The number count in the array
+ */
+extern void quick_sort(int *nums, int count);
+
+#endif /* SORT_H_ */
diff --git a/com32/elflink/modules/test.c b/com32/elflink/modules/test.c
new file mode 100644
index 0000000..82df642
--- /dev/null
+++ b/com32/elflink/modules/test.c
@@ -0,0 +1,111 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * linux.c
+ *
+ * Sample module to load Linux kernels.  This module can also create
+ * a file out of the DHCP return data if running under PXELINUX.
+ *
+ * If -dhcpinfo is specified, the DHCP info is written into the file
+ * /dhcpinfo.dat in the initramfs.
+ *
+ * Usage: linux.c32 [-dhcpinfo] kernel arguments...
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <console.h>
+#include <syslinux/loadfile.h>
+#include <syslinux/linux.h>
+#include <sys/module.h>
+
+//const char *progname = "test.c32";
+
+static int test_main(int argc, char *argv[])
+{
+    const char *kernel_name;
+    struct initramfs *initramfs;
+    char *cmdline;
+    char *boot_image;
+    void *kernel_data;
+    size_t kernel_len;
+    bool opt_quiet = false;
+    char **argp, *arg, *p;
+
+	/*
+    while ((arg = *argp) && arg[0] == '-') {
+	if (!strcmp("-dhcpinfo", arg)) {
+		printf("haha\n");
+	} else {
+	    fprintf(stderr, "%s: unknown option: %s\n", progname, arg);
+	    return 1;
+	}
+	argp++;
+    }
+
+    if (!arg) {
+	fprintf(stderr, "%s: missing kernel name\n", progname);
+	return 1;
+    }
+    */
+
+    //kernel_name = arg;
+    kernel_name = "vmlinuz";
+
+	printf("Loading %s... ", kernel_name);
+    if (loadfile(kernel_name, &kernel_data, &kernel_len)) {
+	printf("failed!\n");
+	goto bail;
+    }
+	printf("ok\n");
+
+    /* Initialize the initramfs chain */
+    initramfs = initramfs_init();
+    if (!initramfs)
+	goto bail;
+
+
+	if (initramfs_load_archive(initramfs, "initrd.gz"))
+		mp("initrd load failure\n");
+	else
+		mp("initrd load success\n");
+
+	mp("1111");
+
+        /* This should not return... */
+    syslinux_boot_linux(kernel_data, kernel_len, initramfs, cmdline);
+
+bail:
+    fprintf(stderr, "Kernel load failure (insufficient memory?)\n");
+    return 1;
+}
+
+MODULE_MAIN(test_main);
diff --git a/com32/elflink/modules/unbase64.c b/com32/elflink/modules/unbase64.c
new file mode 100644
index 0000000..86ddfc8
--- /dev/null
+++ b/com32/elflink/modules/unbase64.c
@@ -0,0 +1,91 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2005-2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * unbase64.c
+ *
+ * Convert a string in base64 format to a byte array.
+ */
+
+#include <string.h>
+#include <base64.h>
+#include <sys/module.h>
+
+static int unbase64_init(void)
+{
+    return 0;			// Nothing to do; return success
+}
+
+static const unsigned char _base64chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+size_t unbase64(unsigned char *buffer, size_t bufsiz, const char *txt)
+{
+    unsigned int bits = 0;
+    int nbits = 0;
+    char base64tbl[256];
+    int i;
+    char v;
+    size_t nbytes = 0;
+
+    memset(base64tbl, -1, sizeof base64tbl);
+
+    for (i = 0; _base64chars[i]; i++) {
+	base64tbl[_base64chars[i]] = i;
+    }
+
+    /* Also support filesystem safe alternate base64 encoding */
+    base64tbl['.'] = 62;
+    base64tbl['-'] = 62;
+    base64tbl['_'] = 63;
+
+    while (*txt) {
+	if ((v = base64tbl[(unsigned char)*txt]) >= 0) {
+	    bits <<= 6;
+	    bits += v;
+	    nbits += 6;
+	    if (nbits >= 8) {
+		if (nbytes < bufsiz)
+		    *buffer++ = (bits >> (nbits - 8));
+		nbytes++;
+		nbits -= 8;
+	    }
+	}
+	txt++;
+    }
+
+    return nbytes;
+}
+
+static void unbase64_exit(void)
+{
+    // Nothing to do
+}
+
+// Define entry and exit points.
+MODULE_INIT(unbase64_init);
+MODULE_EXIT(unbase64_exit);
diff --git a/com32/elflink/test_com32.c b/com32/elflink/test_com32.c
new file mode 100644
index 0000000..19089db
--- /dev/null
+++ b/com32/elflink/test_com32.c
@@ -0,0 +1,208 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <console.h>
+#include <string.h>
+
+#include <sys/module.h>
+#include <sys/exec.h>
+
+#define INFO_PRINT(fmt, args...)	printf("[COM32] " fmt, ##args)
+
+#define MAX_COMMAND_SIZE 	80	// Maximum size of the cmd line
+#define COMMAND_DELIM		" \t\n"	// Whitespace delimiters
+#define MAX_COMMAND_ARGS	(MAX_COMMAND_SIZE/2)	// Maximum argument count for
+												 // program execution
+
+/**
+ * print_help - Display usage instructions on the screen.
+ */
+static void print_help(void)
+{
+    printf("List of available commands:\n");
+    printf("exit - exits the program\n");
+    printf("help - shows this message\n");
+    printf("load <library>... - loads the libraries into the environment\n");
+    printf("spawn <executable> <args> - launches an executable module\n");
+    printf
+	("unload <library>... - unloads the libraries from the environment\n");
+    printf("list - prints the currently loaded modules\n");
+}
+
+/**
+ * print_prompt - Display the command prompt.
+ */
+static void print_prompt(void)
+{
+    printf("\nelflink> ");
+}
+
+/**
+ * read_command - Read a new command from the standard input.
+ * @cmd: the buffer to store the command
+ * @size: the maximum size of the string that can be stored in the buffer
+ *
+ * If the command is larger than the specified size, it is truncated.
+ */
+static void read_command(char *cmd, int size)
+{
+    char *nl = NULL;
+    fgets(cmd, size, stdin);
+
+    // Strip the newline
+    nl = strchr(cmd, '\n');
+
+    if (nl != NULL)
+	*nl = '\0';
+}
+
+/**
+ * process_spawn - Handles the execution of a 'spawn' command.
+ *
+ * The command line is in the internal buffer of strtok.
+ */
+static void process_spawn(void)
+{
+    // Compose the command line
+    char **cmd_line = malloc((MAX_COMMAND_ARGS + 1) * sizeof(char *));
+    int argc = 0, result;
+    char *crt_arg;
+
+    do {
+	crt_arg = strtok(NULL, COMMAND_DELIM);
+	if (crt_arg != NULL && strlen(crt_arg) > 0) {
+	    cmd_line[argc] = crt_arg;
+	    argc++;
+	} else {
+	    break;
+	}
+    } while (argc < MAX_COMMAND_ARGS);
+
+    cmd_line[argc] = NULL;
+
+    if (cmd_line[0] == NULL) {
+	printf("You must specify an executable module.\n");
+    } else {
+	result = spawnv(cmd_line[0], cmd_line);
+
+	printf("Spawn returned %d\n", result);
+    }
+
+    free(cmd_line);
+}
+
+/**
+ * process_library - Handles the execution of the 'load' and 'unload' commands.
+ * @load: contains 1 if the libraries are to be loaded, 0 for unloading.
+ *
+ * The command line is in the internal buffer of strtok.
+ */
+static void process_library(int load)
+{
+    char *crt_lib;
+    int result;
+
+    while ((crt_lib = strtok(NULL, COMMAND_DELIM)) != NULL) {
+	if (strlen(crt_lib) > 0) {
+	    if (load)
+		result = load_library(crt_lib);
+	    else
+		result = unload_library(crt_lib);
+
+	    if (result == 0) {
+		printf("Library '%s' %sloaded successfully.\n", crt_lib,
+		       load ? "" : "un");
+	    } else {
+		printf("Could not %sload library '%s': error %d\n",
+		       load ? "" : "un", crt_lib, result);
+	    }
+	}
+    }
+}
+
+/**
+ * process_list - Handles the execution of the 'list' command.
+ *
+ */
+static void process_list(void)
+{
+    struct elf_module *module;
+    struct module_dep *crt_dep;
+
+    for_each_module(module) {
+	printf("%s (%dK, %s, %s) : ", module->name, module->module_size >> 10,
+	       module->shallow ? "shallow" : "regular",
+	       module->main_func == NULL ? "library" : "program");
+
+	list_for_each_entry(crt_dep, &module->required, list) {
+	    printf("%s ", crt_dep->module->name);
+	}
+
+	printf("\n");
+    }
+}
+
+/**
+ * process_command - Recognizes the requested command and executes it.
+ * @cmd: the command to be executed.
+ *
+ * Returns 1 if the command was 'exit', 0 otherwise.
+ */
+static int process_command(char *cmd)
+{
+    char *cmd_name;
+
+    cmd_name = strtok(cmd, COMMAND_DELIM);
+
+    if (strcmp(cmd_name, "exit") == 0) {
+	printf("Goodbye!\n");
+	return 1;
+    } else if (strcmp(cmd_name, "help") == 0) {
+	print_help();
+    } else if (strcmp(cmd_name, "load") == 0) {
+	process_library(1);
+    } else if (strcmp(cmd_name, "spawn") == 0) {
+	process_spawn();
+    } else if (strcmp(cmd_name, "unload") == 0) {
+	process_library(0);
+    } else if (strcmp(cmd_name, "list") == 0) {
+	process_list();
+    } else {
+	printf("Unknown command. Type 'help' for a list of valid commands.\n");
+    }
+
+    return 0;
+}
+
+/**
+ * The entry point of 'test_com32' COM module.
+ */
+int main(int argc, char **argv)
+{
+    int done = 0;
+    int res;
+    char command[MAX_COMMAND_SIZE] = { 0 };
+
+    // Open a standard r/w console
+    openconsole(&dev_stdcon_r, &dev_stdcon_w);
+
+    res = exec_init();
+    if (res != 0) {
+	printf("Failed to initialize the execution environment.\n");
+	return res;
+    } else {
+	printf("Execution environment initialized successfully.\n");
+    }
+
+    printf("\nFor a list of available commands, type 'help'.\n");
+
+    do {
+	print_prompt();
+	read_command(command, MAX_COMMAND_SIZE);
+	done = process_command(command);
+
+    } while (!done);
+
+    exec_term();
+
+    return 0;
+}
diff --git a/com32/elflink/test_memalign.c b/com32/elflink/test_memalign.c
new file mode 100644
index 0000000..1bd5ba5
--- /dev/null
+++ b/com32/elflink/test_memalign.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <console.h>
+#include <errno.h>
+
+void perform_allocation(size_t align)
+{
+    int res = 0;
+    int size = 100;
+    void *ptr;
+
+    printf("Allocation aligned at %#zx bytes: ", align);
+    res = posix_memalign(&ptr, align, size);
+
+    switch (res) {
+    case 0:
+	printf("address %p\n", ptr);
+	break;
+    case EINVAL:
+	printf("EINVAL\n");
+	break;
+    case ENOMEM:
+	printf("ENOMEM\n");
+	break;
+    }
+}
+
+int main(void)
+{
+    size_t align = 0x10000;
+
+    // Open a standard r/w console
+    openconsole(&dev_stdcon_r, &dev_stdcon_w);
+
+    while (align >= sizeof(void *)) {
+	perform_allocation(align);
+	align /= 2;
+    }
+
+    printf("\n");
+
+    while (align <= 0x10000) {
+	perform_allocation(align);
+	align *= 2;
+    }
+
+    return 0;
+}
diff --git a/com32/include/linux/list.h b/com32/include/linux/list.h
new file mode 100644
index 0000000..3b92e25
--- /dev/null
+++ b/com32/include/linux/list.h
@@ -0,0 +1,463 @@
+// This list structure implementation is adapted from the list implementation
+// on the Linux kernel.
+
+// Original source:
+// http://git.kernel.org/?p=linux/kernel/git/stable/linux-2.6.25.y.git;a=blob_plain;f=include/linux/list.h;hb=HEAD
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:        the pointer to the member.
+ * @type:       the type of the container struct this is embedded in.
+ * @member:     the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = NULL;
+	entry->prev = NULL;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next)
+
+/**
+ * __list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); \
+        	pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+	for (pos = (head)->prev, n = pos->prev; \
+	     pos != (head); \
+	     pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)		\
+	for (pos = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) 			\
+	for (; &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member), 		\
+		n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) 			\
+	for (n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+
+#endif
diff --git a/com32/include/sys/exec.h b/com32/include/sys/exec.h
new file mode 100644
index 0000000..31b62be
--- /dev/null
+++ b/com32/include/sys/exec.h
@@ -0,0 +1,134 @@
+/*
+ * exec.h
+ *
+ *  Created on: Aug 14, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#ifndef EXEC_H_
+#define EXEC_H_
+
+#include <sys/module.h>
+#include <stdlib.h>
+
+/**
+ * EXEC_ROOT_NAME - The name of the ELF module associated with the COM32 module.
+ *
+ * This is a shallow ELF module, that contains only the symbol table for
+ * the code and data sections of the loaded COM32 root module.
+ */
+#define EXEC_ROOT_NAME			"_root_.c32"
+
+/**
+ * MODULES_DEP - The name of the standard module dependency file
+ *
+ * This is the file which contains information about the module dependency
+ * graph ( what other modules it depends on ). The file format is identical
+ * to the standard linux modules.dep file... for more information check out the
+ * man page ).
+ */
+#define MODULES_DEP "modules.dep"
+
+/**
+ * spawn_load - Load a library module or executes an executable one
+ * @name	the name of the library/executable to use, including the extension
+ * 			(e.g. 'sort.c32')
+ * @argv:	a NULL-terminated vector of string arguments, starting with
+ * 			the program name.
+ *
+ * This procedure in essence loads takes the name of a module and checks to see what
+ * kind of module it is ( executable or library ), after which is performs the
+ * appropriate action, either spawning or simply loading the module into memory.
+ */
+extern int spawn_load(const char *name,const char **argv);
+
+extern int module_load_dependencies(const char*name,const char*dep_file);
+
+/**
+ * exec_init - Initialize the dynamic execution environment.
+ *
+ * Among others, it initializes the module subsystem and loads the root
+ * module into memory. You should note the difference between the module
+ * management API, and the execution API:
+ *  - the module system is a static one - it only manages the data structures
+ *  and their relationship. It does not describe the way modules are executed,
+ *  when and how they are loaded/unloaded, etc. It acts as a service layer for
+ *  the execution API.
+ *  - the execution environment is the dynamic part of the SYSLINUX dynamic
+ *  module API - it implements the behavior of the modules: it
+ *  triggers the execution of initialization and termination functions for
+ *  libraries, executes the modules marked as executable, handles dynamic
+ *  memory cleanup, etc. In other words, at this layer the code and data
+ *  loaded by the lower module layer gets to be executed by the CPU,
+ *  thus becoming part of the SYSLINUX environment.
+ */
+extern int exec_init(void);
+
+
+/**
+ * load_library - Loads a dynamic library into the environment.
+ * @name: 	the name of the library to load, including the extension
+ * 			(e.g. 'sort.c32')
+ *
+ * A dynamic library is an ELF module that may contain initialization and
+ * termination routines, but not a main routine. At the same time, any memory
+ * allocations using malloc() and its derivatives are made on behalf of the
+ * currently executing program or the COM32 root module. If the library
+ * is unloaded, no memory cleanup is performed.
+ */
+extern int load_library(const char *name);
+
+/**
+ * unload_library - unloads a library from the environment.
+ * @name:	the name of the library to unload, including the extension
+ * 			(e.g. 'sort.c32')
+ *
+ * Note that no memory allocated by the library code is cleaned up, as the
+ * allocations belong to the innermost calling program in the call stack.
+ */
+extern int unload_library(const char *name);
+
+/**
+ * spawnv - Executes a program in the current environment.
+ * @name:	the name of the program to spawn, including the extension
+ * 			(e.g. 'hello.c32')
+ * @argv:	a NULL-terminated vector of string arguments, starting with
+ * 			the program name.
+ *
+ * A program is an ELF module that contains a main routine. A program is
+ * loaded into memory, executed, then unloaded, thus remaining in memory only
+ * while the main() function is executing. A program also defines a
+ * memory allocation context, and a simple garbage collection mechanism
+ * it thus provided. This is done by internally associating with the program
+ * module each pointer returned by malloc(). After the program finishes
+ * its execution, all the unallocated memory pertaining to the program
+ * is automatically cleaned up.
+ *
+ * Note that this association takes place both for the allocations happening
+ * directly in the program, or indirectly through a library function. Libraries
+ * do not create allocation contexts, thus each allocation they made belong
+ * to the innermost calling program.
+ */
+extern int spawnv(const char *name, const char **argv);
+
+/**
+ * spawnl - Executes a program in the current environment.
+ * @name:	the name of the program to spawn, including the extension
+ * 			(e.g. 'hello.c32')
+ * @arg:	the first argument (argv[0]) to be passed to the main function
+ * 			of the program
+ * @...:	optional subsequent arguments that are passed o the main function
+ * 			of the program
+ *
+ * This is another version of the spawn routine. Please see 'spawnv' for
+ * a full presentation.
+ */
+extern int spawnl(const char *name, const char *arg, ...);
+
+/**
+ * exec_term - Releases the resources of the execution environment.
+ */
+extern void exec_term(void);
+
+
+#endif /* EXEC_H_ */
diff --git a/com32/include/sys/module.h b/com32/include/sys/module.h
new file mode 100644
index 0000000..d7b50b8
--- /dev/null
+++ b/com32/include/sys/module.h
@@ -0,0 +1,377 @@
+/**
+ * syslinux/module.h
+ *
+ * Dynamic ELF modules definitions and services.
+ */
+
+
+#ifndef MODULE_H_
+#define MODULE_H_
+
+#include <stdio.h>
+#include <elf.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <linux/list.h>
+
+
+
+/*
+ * The maximum length of the module file name (including path), stored
+ * in the struct module descriptor.
+ */
+#define MODULE_NAME_SIZE		256
+
+/*
+ * Some common information about what kind of modules we're dealing with
+ */
+#define UNKNOWN_MODULE			-1
+#define EXEC_MODULE			0		
+#define LIB_MODULE			1
+
+/*
+ * Initialization and finalization function signatures
+ */
+
+
+/**
+ * module_init_t - pointer to a initialization routine
+ *
+ * The initialization routine is called after all module constructors were invoked.
+ * It takes no parameters and returns 0 if the module was initialized successfully,
+ * or a non-zero value if errors have occurred.
+ */
+typedef int (*module_init_t)(void);
+
+/**
+ * module_exit_t - pointer to a finalization routine
+ *
+ * The finalization routine is called before the module destructors are to be invoked.
+ * It simply executes some cleanup code, without error reporting.
+ */
+typedef void (*module_exit_t)(void);
+
+/**
+ * module_main_t - pointer to an entry routine
+ *
+ * The entry routine is present only in executable modules, and represents
+ * the entry point for the program.
+ */
+typedef int (*module_main_t)(int, char**);
+
+
+/**
+ * struct elf_module - structure encapsulating a module loaded in memory.
+ *
+ * Each SYSLINUX ELF module must have an associated struct elf_module descriptor
+ * that keeps track of memory allocations, symbol information, and various other
+ * resources needed by the module itself or by other modules that depend on it.
+ *
+ * There are two types of modules:
+ *  - regular modules, which are actual memory images of a loaded & linked shared
+ *  object (ELF file). Memory is reserved for the struct elf_module structure itself
+ *  and for the object loadable sections read from the file.
+ *  - shallow modules, which are not associated with an ELF shared object, but contain
+ *  metainformation about a memory region already present and containing the
+ *  actual code and data. One particular usage of shallow modules is to access
+ *  symbol information from the root COM32 module loaded by the SYSLINUX core.
+ *  As their name suggests, memory is reserved only for the elf_module structure
+ *  itself and optionally for a usually small memory region containing metainformation
+ *  (symbol information).
+ *
+ *  Module descriptors are related to each other through dependency information. A module
+ *  can depend on symbols from other modules, and in turn it can provide symbols used
+ *  by other dependant modules. This relationship can be described as a directed
+ *  acyclic graph (DAG). The graph is stored using double linked lists of
+ *  predecessors and successors. There is also a global linked list containing all
+ *  the modules currently loaded.
+ */
+struct atexit;
+struct elf_module {
+	char				name[MODULE_NAME_SIZE]; 		// The module name
+
+	bool			shallow;	// Whether the module contains any code
+
+	struct list_head	required;		// Head of the required modules list
+	struct list_head	dependants;		// Head of module dependants list
+	struct list_head	list;		// The list entry in the module list
+
+	module_init_t		*init_func;	// The initialization entry point
+	module_exit_t		*exit_func;	// The module finalization code
+	module_main_t		*main_func; // The main function (for executable modules)
+
+
+	void				*module_addr; // The module location in the memory
+	Elf32_Addr			base_addr;	// The base address of the module
+	Elf32_Word			module_size; // The module size in memory
+
+	Elf32_Word			*hash_table;	// The symbol hash table
+	Elf32_Word			*ghash_table;	// The GNU style hash table
+	char				*str_table;		// The string table
+	void 				*sym_table;		// The symbol table
+	void				*got;			// The Global Offset Table
+	Elf32_Dyn			*dyn_table;		// Dynamic loading information table
+
+	Elf32_Word			strtable_size;	// The size of the string table
+	Elf32_Word			syment_size;	// The size of a symbol entry
+	Elf32_Word			symtable_size;	// The size of the symbol table
+
+
+	union {
+		// Transient - Data available while the module is loading
+		struct {
+			FILE		*_file;		// The file object of the open file
+			Elf32_Off	_cr_offset;	// The current offset in the open file
+		} l;
+
+		// Process execution data
+		struct {
+			jmp_buf		process_exit;	// Exit state
+			struct atexit  *atexit_list;	// atexit() chain
+		} x;
+	} u;
+
+};
+
+/**
+ * struct module_dep - structure encapsulating a module dependency need
+ *
+ * This structure represents an item in a double linked list of predecessors or
+ * successors. The item contents is a pointer to the corresponding module descriptor.
+ */
+struct module_dep {
+	struct list_head	list;		// The list entry in the dependency list
+
+	struct elf_module	*module;	// The target module descriptor
+};
+
+
+
+#ifdef DYNAMIC_MODULE
+
+/*
+ * This portion is included by dynamic (ELF) module source files.
+ */
+
+#if 1
+#define MODULE_INIT(fn)	static module_init_t __module_init \
+	__used __attribute__((section(".ctors_modinit")))  = fn
+
+#define MODULE_EXIT(fn) static module_exit_t __module_exit \
+	__used __attribute__((section(".dtors_modexit")))  = fn
+
+#define MODULE_MAIN(fn) static module_main_t __module_main \
+	__used __attribute__((section(".ctors_modmain")))  = fn
+
+#else
+
+#define MODULE_INIT(fn)	static module_init_t __module_init \
+	__attribute__((section(".ctors_modinit")))  = fn
+
+#define MODULE_EXIT(fn) static module_exit_t __module_exit \
+	__attribute__((section(".dtors_modexit")))  = fn
+
+#define MODULE_MAIN(fn) static module_main_t __module_main \
+	__attribute__((section(".ctors_modmain")))  = fn
+
+#endif
+
+
+#else
+
+/*
+ * This portion is included by the core COM32 module.
+ */
+
+/*
+ * Accepted values for various ELF header parameters found in an ELF dynamic
+ * object.
+ */
+#define MODULE_ELF_CLASS		ELFCLASS32		// 32-bit modules
+#define MODULE_ELF_CLASS_SIZE		32			// Size of a word value
+#define MODULE_ELF_DATA			ELFDATA2LSB		// Word endianess
+#define MODULE_ELF_VERSION		EV_CURRENT		// Object version
+#define MODULE_ELF_TYPE			ET_DYN			// Executable type (shared object - .so)
+#define MODULE_ELF_MACHINE		EM_386			// Target architecture
+
+/**
+ * Names of symbols with special meaning (treated as special cases at linking)
+ */
+#define MODULE_ELF_INIT_PTR		"__module_init_ptr"	// Initialization pointer symbol name
+#define MODULE_ELF_EXIT_PTR		"__module_exit_ptr"	// Finalization pointer symbol name
+#define MODULE_ELF_MAIN_PTR		"__module_main_ptr" // Entry pointer symbol name
+
+/**
+ * modules_head - A global linked list containing all the loaded modules.
+ */
+extern struct list_head modules_head;
+
+
+/**
+ * for_each_module - iterator loop through the list of loaded modules.
+ */
+#define for_each_module(m)	list_for_each_entry(m, &modules_head, list)
+
+/**
+ * modules_init - initialize the module subsystem.
+ *
+ * This function must be called before any module operation is to be performed.
+ */
+extern int modules_init(void);
+
+
+/**
+ * modules_term - releases all resources pertaining to the module subsystem.
+ *
+ * This function should be called after all module operations.
+ */
+extern void modules_term(void);
+
+
+/**
+ * module_alloc - reserves space for a new module descriptor.
+ * @name: 	the file name of the module to be loaded.
+ *
+ * The function simply allocates a new module descriptor and initializes its fields
+ * in order to be used by subsequent loading operations.
+ */
+extern struct elf_module *module_alloc(const char *name);
+
+
+/**
+ * module_load - loads a regular ELF module into memory.
+ * @module:	the module descriptor returned by module_alloc.
+ *
+ * The function reads the module file, checks whether the file has a
+ * valid structure, then loads into memory the code and the data and performs
+ * any symbol relocations. A module dependency is created automatically when the
+ * relocated symbol is defined in a different module.
+ *
+ * The function returns 0 if the operation is completed successfully, and
+ * a non-zero value if an error occurs. Possible errors include invalid module
+ * structure, missing symbol definitions (unsatisfied dependencies) and memory
+ * allocation issues.
+ */
+extern int module_load(struct elf_module *module);
+
+
+/**
+ * module_load_shallow - loads a shallow ELF module into memory.
+ * @module:	the module descriptor returned by module_alloc.
+ *
+ * The function reads the module file, checks whether the file has a valid
+ * structure, then loads into memory the module metadata. The metadata currently
+ * contains a symbol table that describes code & data allocated by other means.
+ * Its current use is to describe the root COM32 module to the rest of the
+ * module subsystem.
+ */
+extern int module_load_shallow(struct elf_module *module, Elf32_Addr base_addr);
+
+/**
+ * module_unload - unloads the module from the system.
+ * @module: the module descriptor structure.
+ *
+ * The function checks to see whether the module can be safely removed, then
+ * it releases all the associated memory. This function can be applied both
+ * for standard modules and for shallow modules.
+ *
+ * A module can be safely removed from the system when no other modules reference
+ * symbols from it.
+ */
+extern int module_unload(struct elf_module *module);
+
+/**
+ * module_unload - unloads the module from the system.
+ * @module: the module descriptor structure.
+ *
+ * This function returns the type of module we're dealing with
+ * either a library module ( LIB_MODULE ), executable module ( EXEC_MODULE ),
+ * or an error ( UNKNOWN_MODULE ). The way it checks teh type is by checking to see
+ * if the module has its main_func set ( in which case it's an executable ). In case
+ * it doesn't it then checks to see if init_func is set ( in which case it's a
+ * library module. If this isn't the case either we don't know what it is so bail out
+ */
+extern int get_module_type(struct elf_module *module);
+
+/**
+ * module_unloadable - checks whether the given module can be unloaded.
+ * @module: the module descriptor structure
+ *
+ * A module can be unloaded from the system when no other modules depend on it,
+ * that is, no symbols are referenced from it.
+ */
+extern int module_unloadable(struct elf_module *module);
+
+/**
+ * module_find - searches for a module by its name.
+ * @name: the name of the module, as it was specified in module_alloc.
+ *
+ * The function returns a pointer to the module descriptor, if found, or
+ * NULL otherwise.
+ */
+extern struct elf_module *module_find(const char *name);
+
+/**
+ * module_find_symbol - searches for a symbol definition in a given module.
+ * @name: the name of the symbol to be found.
+ * @module: the module descriptor structure.
+ *
+ * The function searches the module symbol table for a symbol matching exactly
+ * the name provided. The operation uses the following search algorithms, in this
+ * order:
+ *  - If a GNU hash table is present in the module, it is used to find the symbol.
+ *  - If the symbol cannot be found with the first method (either the hash table
+ *  is not present or the symbol is not found) and if a regular (SysV) hash table
+ *  is present, a search is performed on the SysV hash table. If the symbol is not
+ *  found, NULL is returned.
+ *  - If the second method cannot be applied, a linear search is performed by
+ *  inspecting every symbol in the symbol table.
+ *
+ *  If the symbol is found, a pointer to its descriptor structure is returned, and
+ *  NULL otherwise.
+ */
+extern Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module);
+
+/**
+ * global_find_symbol - searches for a symbol definition in the entire module namespace.
+ * @name: the name of the symbol to be found.
+ * @module: an optional (may be NULL) pointer to a module descriptor variable that
+ * will hold the module where the symbol was found.
+ *
+ * The function search for the given symbol name in all the modules currently
+ * loaded in the system, in the reverse module loading order. That is, the most
+ * recently loaded module is searched first, followed by the previous one, until
+ * the first loaded module is reached.
+ *
+ * If no module contains the symbol, NULL is returned, otherwise the return value is
+ * a pointer to the symbol descriptor structure. If the module parameter is not NULL,
+ * it is filled with the address of the module descriptor where the symbol is defined.
+ */
+extern Elf32_Sym *global_find_symbol(const char *name, struct elf_module **module);
+
+/**
+ * module_get_absolute - converts an memory address relative to a module base address
+ * to its absolute value in RAM.
+ * @addr: the relative address to convert.
+ * @module: the module whose base address is used for the conversion.
+ *
+ * The function returns a pointer to the absolute memory address.
+ */
+static inline void *module_get_absolute(Elf32_Addr addr, struct elf_module *module) {
+	return (void*)(module->base_addr + addr);
+}
+
+/**
+ * syslinux_current - get the current module process
+ */
+extern struct elf_module *__syslinux_current;
+static inline const struct elf_module *syslinux_current(void)
+{
+	return __syslinux_current;
+}
+
+
+#endif // DYNAMIC_MODULE
+
+#endif // MODULE_H_
diff --git a/com32/lib/elf32.ld b/com32/lib/elf32.ld
new file mode 100644
index 0000000..158badb
--- /dev/null
+++ b/com32/lib/elf32.ld
@@ -0,0 +1,184 @@
+/*
+ * Linker script for ELF dynamic loaded modules.
+ */
+
+/* Script for --shared -z combreloc: shared library, combine & sort relocs */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+	      "elf32-i386")
+OUTPUT_ARCH(i386)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0 + SIZEOF_HEADERS;
+  .note.gnu.build-id : { *(.note.gnu.build-id) }
+  .hash           : { *(.hash) }
+  .gnu.hash       : { *(.gnu.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rel.dyn        :
+    {
+      *(.rel.init)
+      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+      *(.rel.fini)
+      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
+      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+      *(.rel.ctors)
+      *(.rel.dtors)
+      *(.rel.got)
+      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+    }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+      *(.rela.ctors)
+      *(.rela.dtors)
+      *(.rela.got)
+      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+    }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0x90909090
+  .plt            : { *(.plt) }
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    KEEP (*(.text.*personality*))
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0x90909090
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0x90909090
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  . = ALIGN(4);
+  .preinit_array     :
+  {
+    KEEP (*(.preinit_array))
+  }
+  .init_array     :
+  {
+     KEEP (*(SORT(.init_array.*)))
+     KEEP (*(.init_array))
+  }
+  .fini_array     :
+  {
+    KEEP (*(.fini_array))
+    KEEP (*(SORT(.fini_array.*)))
+  }
+ 
+  .ctors          :
+  {
+  	__ctors_start = .;
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+    LONG(0x00000000)
+    __module_init_ptr = .;
+    KEEP (*(.ctors_modinit))
+    LONG(0x00000000)
+    __module_main_ptr = .;
+    KEEP (*(.ctors_modmain))
+    LONG(0x00000000)
+  }
+  
+  .dtors          :
+  {
+  	__dtors_start = .;
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+    LONG(0x00000000)
+    __module_exit_ptr = .;
+    KEEP (*(.dtors_modexit))
+    LONG(0x00000000)
+  }
+  
+  .jcr            : { KEEP (*(.jcr)) }
+  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+  .dynamic        : { *(.dynamic) }
+  .got            : { *(.got) }
+  /*. = DATA_SEGMENT_RELRO_END (12, .); -> This gives a "invalid assignment to location counter" error */
+  .got.plt        : { *(.got.plt) }
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    KEEP (*(.gnu.linkonce.d.*personality*))
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  PROVIDE (edata = .);
+  PROVIDE (_edata = .); 
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.
+      FIXME: Why do we need it? When there is no .bss section, we don't
+      pad the .data section.  */
+   . = ALIGN(. != 0 ? 32 / 8 : 1);
+  }
+  . = ALIGN(32 / 8);
+  . = ALIGN(32 / 8);
+  PROVIDE (_end = .); 
+  PROVIDE (end = .);
+  /*. = DATA_SEGMENT_END (.); -> This gives a "invalid assignment to location counter" error */
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* DWARF 3 */
+  .debug_pubtypes 0 : { *(.debug_pubtypes) }
+  .debug_ranges   0 : { *(.debug_ranges) }
+  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+  /DISCARD/ : { *(.eh_frame) }
+}
diff --git a/com32/lib/sys/module/common.c b/com32/lib/sys/module/common.c
new file mode 100755
index 0000000..431f5cd
--- /dev/null
+++ b/com32/lib/sys/module/common.c
@@ -0,0 +1,526 @@
+/*
+ * common.c
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#include <stdio.h>
+#include <elf.h>
+#include <string.h>
+
+#include <linux/list.h>
+#include <sys/module.h>
+
+#include "elfutils.h"
+#include "common.h"
+
+/**
+ * The one and only list of loaded modules
+ */
+LIST_HEAD(modules_head);
+
+// User-space debugging routines
+#ifdef ELF_DEBUG
+void print_elf_ehdr(Elf32_Ehdr *ehdr) {
+	int i;
+
+	fprintf(stderr, "Identification:\t");
+	for (i=0; i < EI_NIDENT; i++) {
+		printf("%d ", ehdr->e_ident[i]);
+	}
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Type:\t\t%u\n", ehdr->e_type);
+	fprintf(stderr, "Machine:\t%u\n", ehdr->e_machine);
+	fprintf(stderr, "Version:\t%u\n", ehdr->e_version);
+	fprintf(stderr, "Entry:\t\t0x%08x\n", ehdr->e_entry);
+	fprintf(stderr, "PHT Offset:\t0x%08x\n", ehdr->e_phoff);
+	fprintf(stderr, "SHT Offset:\t0x%08x\n", ehdr->e_shoff);
+	//fprintf(stderr, "Flags:\t\t%u\n", ehdr->e_flags);
+	//fprintf(stderr, "Header size:\t%u (Structure size: %u)\n", ehdr->e_ehsize,sizeof(Elf32_Ehdr));
+	fprintf(stderr, "phnum: %d shnum: %d\n", ehdr->e_phnum,
+		ehdr->e_shnum);
+}
+
+void print_elf_symbols(struct elf_module *module) {
+	unsigned int i;
+	Elf32_Sym *crt_sym;
+
+	for (i = 1; i < module->symtable_size; i++)
+	{
+		crt_sym = (Elf32_Sym*)(module->sym_table + i*module->syment_size);
+
+		fprintf(stderr,"%s %d\n", module->str_table + crt_sym->st_name, crt_sym->st_value);
+
+	}
+}
+#endif //ELF_DEBUG
+
+/*
+ * Image files manipulation routines
+ */
+
+int image_load(struct elf_module *module)
+{
+	module->u.l._file = fopen(module->name, "rb");
+
+	if (module->u.l._file == NULL) {
+		DBG_PRINT("Could not open object file '%s'\n", module->name);
+		goto error;
+	}
+
+	module->u.l._cr_offset = 0;
+
+	return 0;
+
+error:
+	if (module->u.l._file != NULL) {
+		fclose(module->u.l._file);
+		module->u.l._file = NULL;
+	}
+
+	return -1;
+}
+
+
+int image_unload(struct elf_module *module) {
+	if (module->u.l._file != NULL) {
+		fclose(module->u.l._file);
+		module->u.l._file = NULL;
+
+	}
+	module->u.l._cr_offset = 0;
+
+	return 0;
+}
+
+int image_read(void *buff, size_t size, struct elf_module *module) {
+	size_t result = fread(buff, size, 1, module->u.l._file);
+
+	if (result < 1)
+		return -1;
+
+	module->u.l._cr_offset += size;
+	return 0;
+}
+
+int image_skip(size_t size, struct elf_module *module) {
+	void *skip_buff = NULL;
+	size_t result;
+
+	if (size == 0)
+		return 0;
+
+	skip_buff = malloc(size);
+	result = fread(skip_buff, size, 1, module->u.l._file);
+	free(skip_buff);
+
+	if (result < 1)
+		return -1;
+
+	module->u.l._cr_offset += size;
+	return 0;
+}
+
+int image_seek(Elf32_Off offset, struct elf_module *module) {
+	if (offset < module->u.l._cr_offset) // Cannot seek backwards
+		return -1;
+
+	return image_skip(offset - module->u.l._cr_offset, module);
+}
+
+
+// Initialization of the module subsystem
+int modules_init(void) {
+	return 0;
+}
+
+// Termination of the module subsystem
+void modules_term(void) {
+
+}
+
+// Allocates the structure for a new module
+struct elf_module *module_alloc(const char *name) {
+	struct elf_module *result = malloc(sizeof(struct elf_module));
+
+	memset(result, 0, sizeof(struct elf_module));
+
+	INIT_LIST_HEAD(&result->list);
+	INIT_LIST_HEAD(&result->required);
+	INIT_LIST_HEAD(&result->dependants);
+
+	strncpy(result->name, name, MODULE_NAME_SIZE);
+
+	return result;
+}
+
+struct module_dep *module_dep_alloc(struct elf_module *module) {
+	struct module_dep *result = malloc(sizeof(struct module_dep));
+
+	INIT_LIST_HEAD (&result->list);
+
+	result->module = module;
+
+	return result;
+}
+
+struct elf_module *module_find(const char *name) {
+	struct elf_module *cr_module;
+
+	for_each_module(cr_module) {
+		if (strcmp(cr_module->name, name) == 0)
+			return cr_module;
+	}
+
+	return NULL;
+}
+
+
+// Performs verifications on ELF header to assure that the open file is a
+// valid SYSLINUX ELF module.
+int check_header_common(Elf32_Ehdr *elf_hdr) {
+	// Check the header magic
+	if (elf_hdr->e_ident[EI_MAG0] != ELFMAG0 ||
+		elf_hdr->e_ident[EI_MAG1] != ELFMAG1 ||
+		elf_hdr->e_ident[EI_MAG2] != ELFMAG2 ||
+		elf_hdr->e_ident[EI_MAG3] != ELFMAG3) {
+
+		DBG_PRINT("The file is not an ELF object\n");
+		return -1;
+	}
+
+	if (elf_hdr->e_ident[EI_CLASS] != MODULE_ELF_CLASS) {
+		DBG_PRINT("Invalid ELF class code\n");
+		return -1;
+	}
+
+	if (elf_hdr->e_ident[EI_DATA] != MODULE_ELF_DATA) {
+		DBG_PRINT("Invalid ELF data encoding\n");
+		return -1;
+	}
+
+	if (elf_hdr->e_ident[EI_VERSION] != MODULE_ELF_VERSION ||
+			elf_hdr->e_version != MODULE_ELF_VERSION) {
+		DBG_PRINT("Invalid ELF file version\n");
+		return -1;
+	}
+
+	if (elf_hdr->e_machine != MODULE_ELF_MACHINE) {
+		DBG_PRINT("Invalid ELF architecture\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int enforce_dependency(struct elf_module *req, struct elf_module *dep) {
+	struct module_dep *crt_dep;
+	struct module_dep *new_dep;
+
+	list_for_each_entry(crt_dep, &req->dependants, list) {
+		if (crt_dep->module == dep) {
+			// The dependency is already enforced
+			return 0;
+		}
+	}
+
+	new_dep = module_dep_alloc(req);
+	list_add(&new_dep->list, &dep->required);
+
+	new_dep = module_dep_alloc(dep);
+	list_add(&new_dep->list, &req->dependants);
+
+	return 0;
+}
+
+int clear_dependency(struct elf_module *req, struct elf_module *dep) {
+	struct module_dep *crt_dep = NULL;
+	int found = 0;
+
+	list_for_each_entry(crt_dep, &req->dependants, list) {
+		if (crt_dep->module == dep) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		list_del(&crt_dep->list);
+		free(crt_dep);
+	}
+
+	found = 0;
+
+	list_for_each_entry(crt_dep, &dep->required, list) {
+		if (crt_dep->module == req) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		list_del(&crt_dep->list);
+		free(crt_dep);
+	}
+
+	return 0;
+}
+
+int check_symbols(struct elf_module *module)
+{
+	unsigned int i;
+	Elf32_Sym *crt_sym = NULL, *ref_sym = NULL;
+	char *crt_name;
+	struct elf_module *crt_module;
+
+	int strong_count;
+	int weak_count;
+
+	for(i = 1; i < module->symtable_size; i++)
+	{
+		crt_sym = (Elf32_Sym*)(module->sym_table + i * module->syment_size);
+		crt_name = module->str_table + crt_sym->st_name;
+
+		strong_count = 0;
+		weak_count = 0;
+
+		for_each_module(crt_module)
+		{
+			ref_sym = module_find_symbol(crt_name, crt_module);
+
+			// If we found a definition for our symbol...
+			if (ref_sym != NULL && ref_sym->st_shndx != SHN_UNDEF)
+			{
+				switch (ELF32_ST_BIND(ref_sym->st_info))
+				{
+					case STB_GLOBAL:
+						strong_count++;
+						break;
+					case STB_WEAK:
+						weak_count++;
+						break;
+				}
+			}
+		}
+
+		if (crt_sym->st_shndx == SHN_UNDEF)
+		{
+			// We have an undefined symbol
+			if (strong_count == 0 && weak_count == 0)
+			{
+				DBG_PRINT("Symbol %s is undefined\n", crt_name);
+				printf("Undef symbol FAIL: %s ",crt_name);
+				return -1;
+			}
+		}
+		else
+		{
+			if (strong_count > 0 && ELF32_ST_BIND(ref_sym->st_info) == STB_GLOBAL)
+			{
+				// It's not an error - at relocation, the most recent symbol
+				// will be considered
+				DBG_PRINT("Info: Symbol %s is defined more than once\n", crt_name);
+			}
+		}
+		//printf("symbol %s laoded from %d\n",crt_name,crt_sym->st_value);
+	}
+
+	return 0;
+}
+
+int module_unloadable(struct elf_module *module) {
+	if (!list_empty(&module->dependants))
+		return 0;
+
+	return 1;
+}
+
+
+// Unloads the module from the system and releases all the associated memory
+int module_unload(struct elf_module *module) {
+	struct module_dep *crt_dep, *tmp;
+	// Make sure nobody needs us
+	if (!module_unloadable(module)) {
+		DBG_PRINT("Module is required by other modules.\n");
+		return -1;
+	}
+
+	// Remove any dependency information
+	list_for_each_entry_safe(crt_dep, tmp, &module->required, list) {
+		clear_dependency(crt_dep->module, module);
+	}
+
+	// Remove the module from the module list
+	list_del_init(&module->list);
+
+	// Release the loaded segments or sections
+	if (module->module_addr != NULL) {
+		elf_free(module->module_addr);
+
+		DBG_PRINT("%s MODULE %s UNLOADED\n", module->shallow ? "SHALLOW" : "",
+				module->name);
+	}
+	// Release the module structure
+	free(module);
+
+	return 0;
+}
+
+
+static Elf32_Sym *module_find_symbol_sysv(const char *name, struct elf_module *module) {
+	unsigned long h = elf_hash((const unsigned char*)name);
+	Elf32_Word *cr_word = module->hash_table;
+
+	Elf32_Word nbucket = *cr_word++;
+	cr_word++; // Skip nchain
+
+	Elf32_Word *bkt = cr_word;
+	Elf32_Word *chn = cr_word + nbucket;
+
+	Elf32_Word crt_index = bkt[h % module->hash_table[0]];
+	Elf32_Sym *crt_sym;
+
+
+	while (crt_index != STN_UNDEF) {
+		crt_sym = (Elf32_Sym*)(module->sym_table + crt_index*module->syment_size);
+
+		if (strcmp(name, module->str_table + crt_sym->st_name) == 0)
+			return crt_sym;
+
+		crt_index = chn[crt_index];
+	}
+
+	return NULL;
+}
+
+static Elf32_Sym *module_find_symbol_gnu(const char *name, struct elf_module *module) {
+	unsigned long h = elf_gnu_hash((const unsigned char*)name);
+
+	// Setup code (TODO: Optimize this by computing only once)
+	Elf32_Word *cr_word = module->ghash_table;
+	Elf32_Word nbucket = *cr_word++;
+	Elf32_Word symbias = *cr_word++;
+	Elf32_Word bitmask_nwords = *cr_word++;
+
+	if ((bitmask_nwords & (bitmask_nwords - 1)) != 0) {
+		DBG_PRINT("Invalid GNU Hash structure\n");
+		return NULL;
+	}
+
+	Elf32_Word gnu_shift = *cr_word++;
+
+	Elf32_Addr *gnu_bitmask = (Elf32_Addr*)cr_word;
+	cr_word += MODULE_ELF_CLASS_SIZE / 32 * bitmask_nwords;
+
+	Elf32_Word *gnu_buckets = cr_word;
+	cr_word += nbucket;
+
+	Elf32_Word *gnu_chain_zero = cr_word - symbias;
+
+	// Computations
+	Elf32_Word bitmask_word = gnu_bitmask[(h / MODULE_ELF_CLASS_SIZE) &
+	                                       (bitmask_nwords - 1)];
+
+	unsigned int hashbit1 = h & (MODULE_ELF_CLASS_SIZE - 1);
+	unsigned int hashbit2 = (h >> gnu_shift) & (MODULE_ELF_CLASS_SIZE - 1);
+
+	if ((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1) {
+		unsigned long rem;
+		Elf32_Word bucket;
+
+		rem = h % nbucket;
+
+		bucket = gnu_buckets[rem];
+
+		if (bucket != 0) {
+			const Elf32_Word* hasharr = &gnu_chain_zero[bucket];
+
+			do {
+				if (((*hasharr ^ h ) >> 1) == 0) {
+					Elf32_Sym *crt_sym = (Elf32_Sym*)(module->sym_table +
+							(hasharr - gnu_chain_zero) * module->syment_size);
+
+					if (strcmp(name, module->str_table + crt_sym->st_name) == 0) {
+						return crt_sym;
+					}
+				}
+			} while ((*hasharr++ & 1u) == 0);
+		}
+	}
+
+	return NULL;
+}
+
+static Elf32_Sym *module_find_symbol_iterate(const char *name,struct elf_module *module)
+{
+
+	unsigned int i;
+	Elf32_Sym *crt_sym;
+
+	for (i=1; i < module->symtable_size; i++)
+	{
+		crt_sym = (Elf32_Sym*)(module->sym_table + i*module->syment_size);
+		if (strcmp(name, module->str_table + crt_sym->st_name) == 0)
+		{
+			return crt_sym;
+		}
+	}
+
+	return NULL;
+}
+
+Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module) {
+	Elf32_Sym *result = NULL;
+
+	if (module->ghash_table != NULL)
+		result = module_find_symbol_gnu(name, module);
+
+	if (result == NULL)
+	{
+		if (module->hash_table != NULL)
+		{
+			//printf("Attempting SYSV Symbol search\n");
+			result = module_find_symbol_sysv(name, module);
+		}
+		else
+		{
+			//printf("Attempting Iterative Symbol search\n");
+			result = module_find_symbol_iterate(name, module);
+		}
+	}
+
+	return result;
+}
+
+Elf32_Sym *global_find_symbol(const char *name, struct elf_module **module) {
+	struct elf_module *crt_module;
+	Elf32_Sym *crt_sym = NULL;
+	Elf32_Sym *result = NULL;
+
+	for_each_module(crt_module) {
+		crt_sym = module_find_symbol(name, crt_module);
+
+		if (crt_sym != NULL && crt_sym->st_shndx != SHN_UNDEF) {
+			switch (ELF32_ST_BIND(crt_sym->st_info)) {
+			case STB_GLOBAL:
+				if (module != NULL) {
+					*module = crt_module;
+				}
+				return crt_sym;
+			case STB_WEAK:
+				// Consider only the first weak symbol
+				if (result == NULL) {
+					if (module != NULL) {
+						*module = crt_module;
+					}
+					result = crt_sym;
+				}
+				break;
+			}
+		}
+	}
+
+	return result;
+}
diff --git a/com32/lib/sys/module/common.h b/com32/lib/sys/module/common.h
new file mode 100644
index 0000000..6259df5
--- /dev/null
+++ b/com32/lib/sys/module/common.h
@@ -0,0 +1,65 @@
+/*
+ * common.h - Common internal operations performed by the module subsystem
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#ifndef COMMON_H_
+#define COMMON_H_
+
+#include <stdio.h>
+
+#include <sys/module.h>
+#include <linux/list.h>
+
+#include "elfutils.h"
+
+
+// Performs an operation and jumps to a given label if an error occurs
+#define CHECKED(res, expr, error)		\
+	do { 								\
+		(res) = (expr);					\
+		if ((res) < 0)					\
+			goto error;					\
+	} while (0)
+
+#define MIN(x,y)	(((x) < (y)) ? (x) : (y))
+#define MAX(x,y)	(((x) > (y)) ? (x) : (y))
+
+//#define ELF_DEBUG
+
+#ifdef ELF_DEBUG
+#define DBG_PRINT(fmt, args...)	fprintf(stderr, "[ELF] " fmt, ##args)
+#else
+#define DBG_PRINT(fmt, args...)	// Expand to nothing
+#endif
+
+// User-space debugging routines
+#ifdef ELF_DEBUG
+extern void print_elf_ehdr(Elf32_Ehdr *ehdr);
+extern void print_elf_symbols(struct elf_module *module);
+#endif //ELF_DEBUG
+
+
+/*
+ * Image files manipulation routines
+ */
+
+extern int image_load(struct elf_module *module);
+extern int image_unload(struct elf_module *module);
+extern int image_read(void *buff, size_t size, struct elf_module *module);
+extern int image_skip(size_t size, struct elf_module *module);
+extern int image_seek(Elf32_Off offset, struct elf_module *module);
+
+extern struct module_dep *module_dep_alloc(struct elf_module *module);
+
+extern int check_header_common(Elf32_Ehdr *elf_hdr);
+
+extern int enforce_dependency(struct elf_module *req, struct elf_module *dep);
+extern int clear_dependency(struct elf_module *req, struct elf_module *dep);
+
+extern int check_symbols(struct elf_module *module);
+
+
+#endif /* COMMON_H_ */
diff --git a/com32/lib/sys/module/elf_module.c b/com32/lib/sys/module/elf_module.c
new file mode 100755
index 0000000..9a9d198
--- /dev/null
+++ b/com32/lib/sys/module/elf_module.c
@@ -0,0 +1,512 @@
+/*
+ * elf_module.c
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <elf.h>
+
+#include <linux/list.h>
+#include <sys/module.h>
+
+#include "elfutils.h"
+#include "common.h"
+
+static int check_header(Elf32_Ehdr *elf_hdr) {
+	int res;
+
+	res = check_header_common(elf_hdr);
+
+	if (res != 0)
+		return res;
+
+	if (elf_hdr->e_type != MODULE_ELF_TYPE) {
+		DBG_PRINT("The ELF file must be a shared object\n");
+		return -1;
+	}
+
+	if (elf_hdr->e_phoff == 0x00000000) {
+		DBG_PRINT("PHT missing\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ *
+ * The implementation assumes that the loadable segments are present
+ * in the PHT sorted by their offsets, so that only forward seeks would
+ * be necessary.
+ */
+static int load_segments(struct elf_module *module, Elf32_Ehdr *elf_hdr) {
+	int i;
+	int res = 0;
+	void *pht = NULL;
+	Elf32_Phdr *cr_pht;
+
+	Elf32_Addr min_addr  = 0x00000000; // Min. ELF vaddr
+	Elf32_Addr max_addr  = 0x00000000; // Max. ELF vaddr
+	Elf32_Word max_align = sizeof(void*); // Min. align of posix_memalign()
+	Elf32_Addr min_alloc, max_alloc;   // Min. and max. aligned allocables
+
+	Elf32_Addr dyn_addr = 0x00000000;
+
+	// Get to the PHT
+	image_seek(elf_hdr->e_phoff, module);
+
+	// Load the PHT
+	pht = malloc(elf_hdr->e_phnum * elf_hdr->e_phentsize);
+	image_read(pht, elf_hdr->e_phnum * elf_hdr->e_phentsize, module);
+
+	// Compute the memory needings of the module
+	for (i=0; i < elf_hdr->e_phnum; i++) {
+		cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize);
+
+		switch (cr_pht->p_type) {
+		case PT_LOAD:
+			if (i == 0) {
+				min_addr = cr_pht->p_vaddr;
+			} else {
+				min_addr = MIN(min_addr, cr_pht->p_vaddr);
+			}
+
+			max_addr = MAX(max_addr, cr_pht->p_vaddr + cr_pht->p_memsz);
+			max_align = MAX(max_align, cr_pht->p_align);
+			break;
+		case PT_DYNAMIC:
+			dyn_addr = cr_pht->p_vaddr;
+			break;
+		default:
+			// Unsupported - ignore
+			break;
+		}
+	}
+
+	if (max_addr - min_addr == 0) {
+		// No loadable segments
+		DBG_PRINT("No loadable segments found\n");
+		goto out;
+	}
+
+	if (dyn_addr == 0) {
+		DBG_PRINT("No dynamic information segment found\n");
+		goto out;
+	}
+
+	// The minimum address that should be allocated
+	min_alloc = min_addr - (min_addr % max_align);
+
+	// The maximum address that should be allocated
+	max_alloc = max_addr - (max_addr % max_align);
+	if (max_addr % max_align > 0)
+		max_alloc += max_align;
+
+
+	if (elf_malloc(&module->module_addr,
+			max_align,
+			max_alloc-min_alloc) != 0) {
+
+		DBG_PRINT("Could not allocate segments\n");
+		goto out;
+	}
+
+	module->base_addr = (Elf32_Addr)(module->module_addr) - min_alloc;
+	module->module_size = max_alloc - min_alloc;
+
+	// Zero-initialize the memory
+	memset(module->module_addr, 0, module->module_size);
+
+	for (i = 0; i < elf_hdr->e_phnum; i++) {
+		cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize);
+
+		if (cr_pht->p_type == PT_LOAD) {
+			// Copy the segment at its destination
+			if (cr_pht->p_offset < module->u.l._cr_offset) {
+				// The segment contains data before the current offset
+				// It can be discarded without worry - it would contain only
+				// headers
+				Elf32_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset;
+
+				if (image_read(module_get_absolute(cr_pht->p_vaddr, module) + aux_off,
+						cr_pht->p_filesz - aux_off, module) < 0) {
+					res = -1;
+					goto out;
+				}
+			} else {
+				if (image_seek(cr_pht->p_offset, module) < 0) {
+					res = -1;
+					goto out;
+				}
+
+				if (image_read(module_get_absolute(cr_pht->p_vaddr, module),
+						cr_pht->p_filesz, module) < 0) {
+					res = -1;
+					goto out;
+				}
+			}
+
+			DBG_PRINT("Loadable segment of size 0x%08x copied from vaddr 0x%08x at 0x%08x\n",
+					cr_pht->p_filesz,
+					cr_pht->p_vaddr,
+					(Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module));
+		}
+	}
+
+	// Setup dynamic segment location
+	module->dyn_table = module_get_absolute(dyn_addr, module);
+
+	/*
+	DBG_PRINT("Base address: 0x%08x, aligned at 0x%08x\n", module->base_addr,
+			max_align);
+	DBG_PRINT("Module size: 0x%08x\n", module->module_size);
+	*/
+
+out:
+	// Free up allocated memory
+	if (pht != NULL)
+		free(pht);
+
+	return res;
+}
+
+
+static int prepare_dynlinking(struct elf_module *module) {
+	Elf32_Dyn  *dyn_entry = module->dyn_table;
+
+	while (dyn_entry->d_tag != DT_NULL) {
+		switch (dyn_entry->d_tag) {
+		case DT_NEEDED:
+			// TODO: Manage dependencies here
+			break;
+		case DT_HASH:
+			module->hash_table =
+				(Elf32_Word*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+		case DT_GNU_HASH:
+			module->ghash_table =
+				(Elf32_Word*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+		case DT_STRTAB:
+			module->str_table =
+				(char*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+		case DT_SYMTAB:
+			module->sym_table =
+				module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+		case DT_STRSZ:
+			module->strtable_size = dyn_entry->d_un.d_val;
+			break;
+		case DT_SYMENT:
+			module->syment_size = dyn_entry->d_un.d_val;
+			break;
+		case DT_PLTGOT: // The first entry in the GOT
+			module->got = module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+		}
+
+		dyn_entry++;
+	}
+
+	// Now compute the number of symbols in the symbol table
+	if (module->ghash_table != NULL) {
+		module->symtable_size = module->ghash_table[1];
+	} else {
+		module->symtable_size = module->hash_table[1];
+	}
+
+	return 0;
+}
+
+
+static int perform_relocation(struct elf_module *module, Elf32_Rel *rel) {
+	Elf32_Word *dest = module_get_absolute(rel->r_offset, module);
+
+	// The symbol reference index
+	Elf32_Word sym = ELF32_R_SYM(rel->r_info);
+	unsigned char type = ELF32_R_TYPE(rel->r_info);
+
+	// The symbol definition (if applicable)
+	Elf32_Sym *sym_def = NULL;
+	struct elf_module *sym_module = NULL;
+	Elf32_Addr sym_addr = 0x0;
+
+	if (sym > 0) {
+		// Find out details about the symbol
+
+		// The symbol reference
+		Elf32_Sym *sym_ref =
+			(Elf32_Sym*)(module->sym_table + sym * module->syment_size);
+
+		// The symbol definition
+		sym_def =
+			global_find_symbol(module->str_table + sym_ref->st_name,
+					&sym_module);
+
+		if (sym_def == NULL) {
+			// This should never happen
+			DBG_PRINT("Cannot perform relocation for symbol %s\n",
+					module->str_table + sym_ref->st_name);
+
+			return -1;
+		}
+
+		// Compute the absolute symbol virtual address
+		sym_addr = (Elf32_Addr)module_get_absolute(sym_def->st_value, sym_module);
+
+		if (sym_module != module) {
+			// Create a dependency
+			enforce_dependency(sym_module, module);
+		}
+	}
+
+	switch (type) {
+	case R_386_NONE:
+		// Do nothing
+		break;
+	case R_386_32:
+		*dest += sym_addr;
+		break;
+	case R_386_PC32:
+		*dest += sym_addr - (Elf32_Addr)dest;
+		break;
+	case R_386_COPY:
+		if (sym_addr > 0) {
+			memcpy((void*)dest, (void*)sym_addr, sym_def->st_size);
+		}
+		break;
+	case R_386_GLOB_DAT:
+	case R_386_JMP_SLOT:
+		// Maybe TODO: Keep track of the GOT entries allocations
+		*dest = sym_addr;
+		break;
+	case R_386_RELATIVE:
+		*dest += module->base_addr;
+		break;
+	default:
+		DBG_PRINT("Relocation type %d not supported\n", type);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int resolve_symbols(struct elf_module *module) {
+	Elf32_Dyn  *dyn_entry = module->dyn_table;
+	unsigned int i;
+	int res;
+
+	Elf32_Word plt_rel_size = 0;
+	void *plt_rel = NULL;
+
+	void *rel = NULL;
+	Elf32_Word rel_size = 0;
+	Elf32_Word rel_entry = 0;
+
+	// The current relocation
+	Elf32_Rel *crt_rel;
+
+	while (dyn_entry->d_tag != DT_NULL) {
+		switch(dyn_entry->d_tag) {
+
+		// PLT relocation information
+		case DT_PLTRELSZ:
+			plt_rel_size = dyn_entry->d_un.d_val;
+			break;
+		case DT_PLTREL:
+			if (dyn_entry->d_un.d_val != DT_REL) {
+				DBG_PRINT("Unsupported PLT relocation\n");
+				return -1;
+			}
+		case DT_JMPREL:
+			plt_rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+
+		// Standard relocation information
+		case DT_REL:
+			rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
+			break;
+		case DT_RELSZ:
+			rel_size = dyn_entry->d_un.d_val;
+			break;
+		case DT_RELENT:
+			rel_entry = dyn_entry->d_un.d_val;
+			break;
+
+		// Module initialization and termination
+		case DT_INIT:
+			// TODO Implement initialization functions
+			break;
+		case DT_FINI:
+			// TODO Implement finalization functions
+			break;
+		}
+
+		dyn_entry++;
+	}
+
+	if (rel_size > 0) {
+		// Process standard relocations
+		for (i = 0; i < rel_size/rel_entry; i++) {
+			crt_rel = (Elf32_Rel*)(rel + i*rel_entry);
+
+			res = perform_relocation(module, crt_rel);
+
+			if (res < 0)
+				return res;
+		}
+
+	}
+
+	if (plt_rel_size > 0) {
+		// TODO: Permit this lazily
+		// Process PLT relocations
+		for (i = 0; i < plt_rel_size/sizeof(Elf32_Rel); i++) {
+			crt_rel = (Elf32_Rel*)(plt_rel + i*sizeof(Elf32_Rel));
+
+			res = perform_relocation(module, crt_rel);
+
+			if (res < 0)
+				return res;
+		}
+	}
+
+	return 0;
+}
+
+
+
+static int extract_operations(struct elf_module *module) {
+	Elf32_Sym *init_sym = module_find_symbol(MODULE_ELF_INIT_PTR, module);
+	Elf32_Sym *exit_sym = module_find_symbol(MODULE_ELF_EXIT_PTR, module);
+	Elf32_Sym *main_sym = module_find_symbol(MODULE_ELF_MAIN_PTR, module);
+
+	if (init_sym == NULL) {
+		DBG_PRINT("Cannot find initialization routine pointer.\n");
+		printf("Cannot find initialization routine pointer.\n");
+		return -1;
+	}
+	if (exit_sym == NULL) {
+		DBG_PRINT("Cannot find exit routine pointer.\n");
+		printf("Cannot find exit routine pointer.\n");
+		return -1;
+	}
+	if (main_sym == NULL) {
+		DBG_PRINT("Cannot find main routine pointer.\n");
+		printf("Cannot find main routine pointer.\n");
+		return -1;
+	}
+
+	module->init_func = (module_init_t*)module_get_absolute(
+								init_sym->st_value, module);
+	if (*(module->init_func) == NULL) {
+		module->init_func = NULL;
+	}
+
+	module->exit_func = (module_exit_t*)module_get_absolute(
+								exit_sym->st_value, module);
+	if (*(module->exit_func) == NULL) {
+		module->exit_func = NULL;
+	}
+
+	module->main_func = (module_main_t*)module_get_absolute(
+								main_sym->st_value, module);
+	if (*(module->main_func) == NULL) {
+		module->main_func = NULL;
+	}
+
+
+	return 0;
+}
+
+// Loads the module into the system
+int module_load(struct elf_module *module) {
+	int res;
+	Elf32_Ehdr elf_hdr;
+
+	// Do not allow duplicate modules
+	if (module_find(module->name) != NULL) {
+		DBG_PRINT("Module %s is already loaded.\n", module->name);
+		return -1;
+	}
+
+	// Get a mapping/copy of the ELF file in memory
+	res = image_load(module);
+
+	if (res < 0) {
+		return res;
+	}
+
+	// The module is a fully featured dynamic library
+	module->shallow = 0;
+
+	CHECKED(res, image_read(&elf_hdr, sizeof(Elf32_Ehdr), module), error);
+	//printf("check... 1\n");
+	
+	//print_elf_ehdr(&elf_hdr);
+
+	// Checking the header signature and members
+	CHECKED(res, check_header(&elf_hdr), error);
+	//printf("check... 2\n");
+
+	// Load the segments in the memory
+	CHECKED(res, load_segments(module, &elf_hdr), error);
+	//printf("bleah... 3\n");
+	// Obtain dynamic linking information
+	CHECKED(res, prepare_dynlinking(module), error);
+	//printf("check... 4\n");
+
+	// Check the symbols for duplicates / missing definitions
+	CHECKED(res, check_symbols(module), error);
+	//printf("check... 5\n");
+
+	// Obtain constructors and destructors
+	CHECKED(res, extract_operations(module), error);
+	//printf("check... 6\n");
+
+	// Add the module at the beginning of the module list
+	list_add(&module->list, &modules_head);
+
+	// Perform the relocations
+	resolve_symbols(module);
+
+	//mp("module->symtable_size = %d\n", module->symtable_size);
+
+	//print_elf_symbols(module);
+
+	// The file image is no longer needed
+	image_unload(module);
+
+	/*
+	DBG_PRINT("MODULE %s LOADED SUCCESSFULLY (main@%p, init@%p, exit@%p)\n",
+			module->name,
+			(module->main_func == NULL) ? NULL : *(module->main_func),
+			(module->init_func == NULL) ? NULL : *(module->init_func),
+			(module->exit_func == NULL) ? NULL : *(module->exit_func));
+	*/
+
+	return 0;
+
+error:
+	// Remove the module from the module list (if applicable)
+	list_del_init(&module->list);
+
+	if (module->module_addr != NULL) {
+		elf_free(module->module_addr);
+		module->module_addr = NULL;
+	}
+
+	image_unload(module);
+
+	// Clear the execution part of the module buffer
+	memset(&module->u, 0, sizeof module->u);
+
+	return res;
+}
+
diff --git a/com32/lib/sys/module/elfutils.c b/com32/lib/sys/module/elfutils.c
new file mode 100644
index 0000000..0e65317
--- /dev/null
+++ b/com32/lib/sys/module/elfutils.c
@@ -0,0 +1,89 @@
+#include <stdlib.h>
+#include <errno.h>
+
+#include "elfutils.h"
+
+unsigned long elf_hash(const unsigned char *name) {
+	unsigned long h = 0;
+	unsigned long g;
+
+	while (*name) {
+		h = (h << 4) + *name++;
+		if ((g = h & 0xF0000000))
+			h ^= g >> 24;
+
+		h &= ~g;
+	}
+
+	return h;
+}
+
+unsigned long elf_gnu_hash(const unsigned char *name) {
+	unsigned long h = 5381;
+	unsigned char c;
+
+	for (c = *name; c != '\0'; c = *++name) {
+		h = h * 33 + c;
+	}
+
+	return h & 0xFFFFFFFF;
+}
+
+#ifdef ELF_NO_POSIX_MEMALIGN
+
+struct memalign_info {
+	void 	*start_addr;
+	char	data[0];
+};
+
+int elf_malloc(void **memptr, size_t alignment, size_t size) {
+	void *start_addr = NULL;
+	struct memalign_info *info;
+
+	if ((alignment & (alignment - 1)) != 0)
+		return EINVAL;
+	if (alignment % sizeof(void*) != 0)
+		alignment = sizeof(void*);
+
+	start_addr = malloc(size + (alignment > sizeof(struct memalign_info) ?
+					alignment : sizeof(struct memalign_info)));
+
+	if (start_addr == NULL)
+		return ENOMEM;
+
+
+	info = (struct memalign_info*)(start_addr -
+			((unsigned long)start_addr % alignment) +
+			alignment - sizeof(struct memalign_info));
+
+	info->start_addr = start_addr;
+
+	*memptr = info->data;
+
+	return 0;
+}
+
+void elf_free(void *memptr) {
+	struct memalign_info *info = (struct memalign_info*)(memptr -
+			sizeof(struct memalign_info));
+
+	free(info->start_addr);
+}
+
+#else
+
+int elf_malloc(void **memptr, size_t alignment, size_t size) {
+	if ((alignment & (alignment - 1)) != 0)
+		return EINVAL;
+
+	if (alignment % sizeof(void*) != 0)
+		alignment = sizeof(void*);
+
+	return posix_memalign(memptr, alignment, size);
+}
+
+void elf_free(void *memptr) {
+	free(memptr);
+}
+
+#endif //ELF_NO_POSIX_MEMALIGN
diff --git a/com32/lib/sys/module/elfutils.h b/com32/lib/sys/module/elfutils.h
new file mode 100644
index 0000000..b18968f
--- /dev/null
+++ b/com32/lib/sys/module/elfutils.h
@@ -0,0 +1,64 @@
+#ifndef ELF_UTILS_H_
+#define ELF_UTILS_H_
+
+#include <elf.h>
+#include <stdlib.h>
+
+/**
+ * elf_get_header - Returns a pointer to the ELF header structure.
+ * @elf_image: pointer to the ELF file image in memory
+ */
+static inline Elf32_Ehdr *elf_get_header(void *elf_image) {
+	return (Elf32_Ehdr*)elf_image;
+}
+
+/**
+ * elf_get_pht - Returns a pointer to the first entry in the PHT.
+ * @elf_image: pointer to the ELF file image in memory
+ */
+static inline Elf32_Phdr *elf_get_pht(void *elf_image) {
+	Elf32_Ehdr *elf_hdr = elf_get_header(elf_image);
+
+	return (Elf32_Phdr*)((Elf32_Off)elf_hdr + elf_hdr->e_phoff);
+}
+
+//
+/**
+ * elf_get_ph - Returns the element with the given index in the PTH
+ * @elf_image: pointer to the ELF file image in memory
+ * @index: the index of the PHT entry to look for
+ */
+static inline Elf32_Phdr *elf_get_ph(void *elf_image, int index) {
+	Elf32_Phdr *elf_pht = elf_get_pht(elf_image);
+	Elf32_Ehdr *elf_hdr = elf_get_header(elf_image);
+
+	return (Elf32_Phdr*)((Elf32_Off)elf_pht + index * elf_hdr->e_phentsize);
+}
+
+/**
+ * elf_hash - Returns the index in a SysV hash table for the symbol name.
+ * @name: the name of the symbol to look for
+ */
+extern unsigned long elf_hash(const unsigned char *name);
+
+/**
+ * elf_gnu_hash - Returns the index in a GNU hash table for the symbol name.
+ * @name: the name of the symbol to look for
+ */
+extern unsigned long elf_gnu_hash(const unsigned char *name);
+
+/**
+ * elf_malloc - Allocates memory to be used by ELF module contents.
+ * @memptr: pointer to a variable to hold the address of the allocated block.
+ * @alignment: alignment constraints of the block
+ * @size: the required size of the block
+ */
+extern int elf_malloc(void **memptr, size_t alignment, size_t size);
+
+/**
+ * elf_free - Releases memory previously allocated by elf_malloc.
+ * @memptr: the address of the allocated block
+ */
+extern void elf_free(void *memptr);
+
+#endif /*ELF_UTILS_H_*/
diff --git a/com32/lib/sys/module/exec.c b/com32/lib/sys/module/exec.c
new file mode 100644
index 0000000..d6942a9
--- /dev/null
+++ b/com32/lib/sys/module/exec.c
@@ -0,0 +1,400 @@
+/*
+ * exec.c
+ *
+ *  Created on: Aug 14, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+#include <sys/module.h>
+#include <sys/exec.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <setjmp.h>
+#include <alloca.h>
+
+#define DBG_PRINT(fmt, args...)	fprintf(stderr, "[EXEC] " fmt, ##args)
+
+static struct elf_module    *mod_root = NULL;
+struct elf_module *__syslinux_current = NULL;
+
+int exec_init(void)
+{
+	int res;
+
+	res = modules_init();
+	if (res != 0)
+		return res;
+
+	// Load the root module
+	mod_root = module_alloc(EXEC_ROOT_NAME);
+
+	if (mod_root == NULL)
+		return -1;
+
+	res = module_load_shallow(mod_root, 0);
+	if (res != 0) {
+		mod_root = NULL;
+		return res;
+	}
+
+	return 0;
+}
+
+int get_module_type(struct elf_module *module)
+{
+	if(module->main_func) return EXEC_MODULE;
+	else if(module->init_func) return LIB_MODULE;
+	return UNKNOWN_MODULE;
+}
+
+int load_library(const char *name)
+{
+	int res;
+	struct elf_module *module = module_alloc(name);
+
+	if (module == NULL)
+		return -1;
+
+	res = module_load(module);
+	if (res != 0) {
+		module_unload(module);
+		return res;
+	}
+
+	if (module->main_func != NULL) {
+		DBG_PRINT("Cannot load executable module as library.\n");
+		module_unload(module);
+		return -1;
+	}
+
+	if (module->init_func != NULL) {
+		res = (*(module->init_func))();
+		DBG_PRINT("Initialization function returned: %d\n", res);
+	} else {
+		DBG_PRINT("No initialization function present.\n");
+	}
+
+	if (res != 0) {
+		module_unload(module);
+		return res;
+	}
+
+	return 0;
+}
+
+int unload_library(const char *name)
+{
+	int res;
+	struct elf_module *module = module_find(name);
+
+	if (module == NULL)
+		return -1;
+
+	if (!module_unloadable(module)) {
+		return -1;
+	}
+
+	if (module->exit_func != NULL) {
+		(*(module->exit_func))();
+	}
+
+	res = module_unload(module);
+	return res;
+}
+
+jmp_buf __process_exit_jmp;
+
+#if 0
+int spawnv(const char *name, const char **argv)
+{
+	int res, ret_val = 0;
+	const char **arg;
+	int argc;
+	char **argp, **args;
+	struct elf_module *previous;
+	malloc_tag_t prev_mem_tag;
+
+	struct elf_module *module = module_alloc(name);
+
+	if (module == NULL)
+		return -1;
+
+	res = module_load(module);
+	if (res != 0) {
+		module_unload(module);
+		return res;
+	}
+
+	if (module->main_func == NULL) {
+		// We can't execute without a main function
+		module_unload(module);
+		return -1;
+	}
+	/*if (module->main_func != NULL) {
+		const char **last_arg = argv;
+		void *old_tag;
+		while (*last_arg != NULL)
+			last_arg++;
+
+		// Setup the memory allocation context
+		old_tag = __mem_get_tag_global();
+		__mem_set_tag_global(module);
+
+		// Execute the program
+		ret_val = (*(module->main_func))(last_arg - argv, argv);
+
+		// Clean up the allocation context
+		__free_tagged(module);
+		// Restore the allocation context
+		__mem_set_tag_global(old_tag);
+	} else {
+		// We can't execute without a main function
+		module_unload(module);
+		return -1;
+	}*/
+	// Set up the process context
+	previous = __syslinux_current;
+	prev_mem_tag = __mem_get_tag_global();
+
+	// Setup the new process context
+	__syslinux_current = module;
+	__mem_set_tag_global((malloc_tag_t)module);
+
+	// Generate a new process copy of argv (on the stack)
+	argc = 0;
+	for (arg = argv; *arg; arg++)
+		argc++;
+
+	args = alloca((argc+1) * sizeof(char *));
+
+	for (arg = argv, argp = args; *arg; arg++, argp++) {
+		size_t l = strlen(*arg)+1;
+		*argp = alloca(l);
+		memcpy(*argp, *arg, l);
+	}
+
+	*args = NULL;
+
+	// Execute the program
+	ret_val = setjmp(module->u.x.process_exit);
+
+	if (ret_val)
+		ret_val--;		/* Valid range is 0-255 */
+	else if (!module->main_func)
+		ret_val = -1;
+	else
+		exit((*module->main_func)(argc, args)); /* Actually run! */
+
+	// Clean up the allocation context
+	__free_tagged(module);
+	// Restore the allocation context
+	__mem_set_tag_global(prev_mem_tag);
+	// Restore the process context
+	__syslinux_current = previous;
+
+	res = module_unload(module);
+
+	if (res != 0) {
+		return res;
+	}
+
+	return ((unsigned int)ret_val & 0xFF);
+}
+
+int spawnl(const char *name, const char *arg, ...)
+{
+	/*
+	 * NOTE: We assume the standard ABI specification for the i386
+	 * architecture. This code may not work if used in other
+	 * circumstances, including non-variadic functions, different
+	 * architectures and calling conventions.
+	 */
+	return spawnv(name, &arg);
+}
+#endif
+
+struct elf_module *cur_module;
+
+int spawn_load(const char *name,const char **argv)
+{
+	int res, ret_val = 0;
+	const char **arg;
+	int argc;
+	char **argp, **args;
+	struct elf_module *previous;
+	//malloc_tag_t prev_mem_tag;
+	struct elf_module *module = module_alloc(name);
+	struct elf_module *prev_module;
+
+	int type;
+
+	mp("enter: name = %s", name);
+
+	if (module == NULL)
+		return -1;
+
+	/* ugly hack to reload the same module */
+	if (!strcmp(cur_module->name, module->name)) {
+		mp("We is running this module %s already!", module->name);
+		module_unload(cur_module);
+		cur_module = NULL;
+	}
+
+	res = module_load(module);
+	if (res != 0) {
+		module_unload(module);
+		return res;
+	}
+
+	type = get_module_type(module);
+	prev_module = cur_module;
+	cur_module = module;
+
+	mp("type = %d, prev = %s, cur = %s",
+		type, prev_module->name, cur_module->name);
+
+	if(type==LIB_MODULE)
+	{
+		if (module->init_func != NULL) {
+			res = (*(module->init_func))();
+			DBG_PRINT("Initialization function returned: %d\n", res);
+		} else {
+			DBG_PRINT("No initialization function present.\n");
+		}
+
+		if (res != 0) {
+			cur_module = prev_module;
+			module_unload(module);
+			return res;
+		}
+		return 0;
+	}
+	else if(type==EXEC_MODULE)
+	{
+		previous = __syslinux_current;
+		//prev_mem_tag = __mem_get_tag_global();
+
+		// Setup the new process context
+		__syslinux_current = module;
+		//__mem_set_tag_global((malloc_tag_t)module);
+
+		// Generate a new process copy of argv (on the stack)
+		argc = 0;
+		for (arg = argv; *arg; arg++)
+			argc++;
+
+		args = alloca((argc+1) * sizeof(char *));
+
+		for (arg = argv, argp = args; *arg; arg++, argp++) {
+			size_t l = strlen(*arg)+1;
+			*argp = alloca(l);
+			memcpy(*argp, *arg, l);
+		}
+
+		*args = NULL;
+
+		// Execute the program
+		ret_val = setjmp(module->u.x.process_exit);
+
+		if (ret_val)
+			ret_val--;		/* Valid range is 0-255 */
+		else if (!module->main_func)
+			ret_val = -1;
+		else
+			exit((*module->main_func)(argc, args)); /* Actually run! */
+
+		// Clean up the allocation context
+		//__free_tagged(module);
+		// Restore the allocation context
+		//__mem_set_tag_global(prev_mem_tag);
+		// Restore the process context
+		__syslinux_current = previous;
+
+		cur_module = prev_module;
+		res = module_unload(module);
+
+		if (res != 0) {
+			return res;
+		}
+
+		return ((unsigned int)ret_val & 0xFF);
+	}
+	/*
+	module_unload(module);
+	return -1;
+	*/
+}
+
+int module_load_dependencies(const char *name,const char *dep_file)
+{
+	FILE *d_file=fopen(dep_file,"r");
+	char line[2048],aux[2048],temp_name[MODULE_NAME_SIZE],slbz[24];
+	int i=0,j=0,res=0;
+
+	if(d_file==NULL)
+	{
+		DBG_PRINT("Could not open object file '%s'\n",dep_file);
+		return -1;
+	}
+
+	/* Note from feng:
+	 * new modues.dep has line like this:
+	 *	a.c32: b.32 c.c32 d.c32
+	 * with desktop glibc
+	 *	sscanf(line,"%[^:]: %[^\n]", temp_name, aux);
+	 * works, which doesn't work here
+	 */
+	memset(temp_name, 0, sizeof(temp_name));
+	memset(aux, 0, sizeof(aux));
+	while (1) {
+		if(fgets(line,2048,d_file)==NULL)
+			break;
+
+		//sscanf(line,"%s %[^\t\n]s",temp_name,aux);
+		//sscanf(line,"%[^:]: %[^\n]", temp_name, aux);
+		//sscanf(line,"%[^:]: %[^\n]\n", temp_name, aux);
+
+		sscanf(line,"%[^:]:", temp_name);
+		if (!strncmp(name, temp_name, strlen(name))) {
+			/* The next 2 chars should be ':' and ' ' */
+			i = strlen(temp_name);
+			if (line[i] != ':' || line[i+1] != ' ')
+				break;
+
+			i +=2;
+			j = 0;
+			while (line[i] != '\n')
+				aux[j++] = line[i++];
+			aux[j] = '\0';
+			//mp("found dependency: temp_name = %s, aux = %s, name = %s", temp_name, aux, name);
+			break;
+		}
+	}
+	fclose(d_file);
+
+	/* Reuse temp_name for dependent module name buffer */
+	memset(temp_name, 0, sizeof(temp_name));
+	i = 0;
+	while (aux[i]) {
+		sscanf(aux + i, "%s", temp_name);
+		//mp("load module: %s", temp_name);
+		i += strlen(temp_name);
+		i++;	/* skip a space */
+
+		if (strlen(temp_name)) {
+			module_load_dependencies(temp_name, MODULES_DEP);
+			if (spawn_load(temp_name, NULL) < 0)
+				continue;
+		}
+	}
+
+	return 0;
+}
+
+void exec_term(void)
+{
+	modules_term();
+}
diff --git a/com32/lib/sys/module/shallow_module.c b/com32/lib/sys/module/shallow_module.c
new file mode 100644
index 0000000..fbcf781
--- /dev/null
+++ b/com32/lib/sys/module/shallow_module.c
@@ -0,0 +1,161 @@
+/*
+ * shallow_module.c
+ *
+ *  Created on: Aug 11, 2008
+ *      Author: Stefan Bucur <stefanb at zytor.com>
+ */
+
+
+#include <string.h>
+#include <sys/module.h>
+
+#include "common.h"
+#include "elfutils.h"
+
+
+static int check_header_shallow(Elf32_Ehdr *elf_hdr) {
+	int res;
+
+	res = check_header_common(elf_hdr);
+
+	if (res != 0)
+		return res;
+
+	if (elf_hdr->e_shoff == 0x00000000) {
+		DBG_PRINT("SHT missing\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int load_shallow_sections(struct elf_module *module, Elf32_Ehdr *elf_hdr) {
+	int i;
+	int res = 0;
+	void *sht = NULL;
+	void *buffer = NULL;
+	Elf32_Shdr *crt_sht;
+	Elf32_Off buff_offset;
+
+	Elf32_Off min_offset = 0xFFFFFFFF;
+	Elf32_Off max_offset = 0x00000000;
+	Elf32_Word max_align = 0x1;
+
+	Elf32_Off sym_offset = 0xFFFFFFFF;
+	Elf32_Off str_offset = 0xFFFFFFFF;
+
+
+	char *sh_strtable;
+
+	// We buffer the data up to the SHT
+	buff_offset = module->u.l._cr_offset;
+
+	buffer = malloc(elf_hdr->e_shoff - buff_offset);
+	// Get to the SHT
+	image_read(buffer, elf_hdr->e_shoff - buff_offset, module);
+
+	// Load the SHT
+	sht = malloc(elf_hdr->e_shnum * elf_hdr->e_shentsize);
+	image_read(sht, elf_hdr->e_shnum * elf_hdr->e_shentsize, module);
+
+	// Get the string table of the section names
+	crt_sht = (Elf32_Shdr*)(sht + elf_hdr->e_shstrndx * elf_hdr->e_shentsize);
+	sh_strtable = (char*)(buffer + (crt_sht->sh_offset - buff_offset));
+
+	for (i = 0; i < elf_hdr->e_shnum; i++) {
+		crt_sht = (Elf32_Shdr*)(sht + i*elf_hdr->e_shentsize);
+
+		if (strcmp(".symtab", sh_strtable + crt_sht->sh_name) == 0) {
+			// We found the symbol table
+			min_offset = MIN(min_offset, crt_sht->sh_offset);
+			max_offset = MAX(max_offset, crt_sht->sh_offset + crt_sht->sh_size);
+			max_align = MAX(max_align, crt_sht->sh_addralign);
+
+			sym_offset = crt_sht->sh_offset;
+
+			module->syment_size = crt_sht->sh_entsize;
+			module->symtable_size = crt_sht->sh_size / crt_sht->sh_entsize;
+		}
+		if (strcmp(".strtab", sh_strtable + crt_sht->sh_name) == 0) {
+			// We found the string table
+			min_offset = MIN(min_offset, crt_sht->sh_offset);
+			max_offset = MAX(max_offset, crt_sht->sh_offset + crt_sht->sh_size);
+			max_align = MAX(max_align, crt_sht->sh_addralign);
+
+			str_offset = crt_sht->sh_offset;
+
+			module->strtable_size = crt_sht->sh_size;
+		}
+	}
+
+	if (elf_malloc(&module->module_addr, max_align,
+			max_offset - min_offset) != 0) {
+		DBG_PRINT("Could not allocate sections\n");
+		goto out;
+	}
+
+	// Copy the data
+	image_seek(min_offset, module);
+	image_read(module->module_addr, max_offset - min_offset, module);
+
+	// Setup module information
+	module->module_size = max_offset - min_offset;
+	module->str_table = (char*)(module->module_addr + (str_offset - min_offset));
+	module->sym_table = module->module_addr + (sym_offset - min_offset);
+
+out:
+	// Release the SHT
+	if (sht != NULL)
+		free(sht);
+
+	// Release the buffer
+	if (buffer != NULL)
+		free(buffer);
+
+	return res;
+}
+
+
+int module_load_shallow(struct elf_module *module, Elf32_Addr base_addr) {
+	int res;
+	Elf32_Ehdr elf_hdr;
+
+	// Do not allow duplicate modules
+	if (module_find(module->name) != NULL) {
+		DBG_PRINT("Module already loaded.\n");
+		return -1;
+	}
+
+	res = image_load(module);
+
+	if (res < 0)
+		return res;
+
+	module->shallow = 1;
+
+	CHECKED(res, image_read(&elf_hdr, sizeof(Elf32_Ehdr), module), error);
+
+	// Checking the header signature and members
+	CHECKED(res, check_header_shallow(&elf_hdr), error);
+
+	CHECKED(res, load_shallow_sections(module, &elf_hdr), error);
+	module->base_addr = base_addr;
+
+	// Check the symbols for duplicates / missing definitions
+	CHECKED(res, check_symbols(module), error);
+
+	// Add the module at the beginning of the module list
+	list_add(&module->list, &modules_head);
+
+	// The file image is no longer needed
+	image_unload(module);
+
+	DBG_PRINT("SHALLOW MODULE %s LOADED SUCCESSFULLY\n", module->name);
+
+	return 0;
+
+error:
+	image_unload(module);
+
+	return res;
+}



More information about the Syslinux-commits mailing list