diff --git a/.gitmodules b/.gitmodules
index 63add75bd46ec1eb95d0ef9293369e8e118bf75d..4d2493514c11c925f52d4d59a2579cfa03a6da61 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
 [submodule "external/glibc.bin"]
 	path = external/glibc.bin
 	url = ../glibc.bin
+[submodule "external/glibc-testsuite"]
+	path = external/glibc-testsuite
+	url = ../glibc-testsuite
diff --git a/README b/README
index a47a698e98e6ed603a8d28f94a54229e11c8a60b..19282f268428f3a22cabd1d7daa1247a7d4c43ef 100644
--- a/README
+++ b/README
@@ -5,9 +5,7 @@ To build OSv
 2) make sure all git submodules are uptodate
    -----------------------------------------
 
-    git submodule init external/openjdk.bin/
-    git submodule init external/libunwind
-    git submodule update
+    git submodule update --init
 
 1) build the specially patched libunwind
    -------------------------------------
@@ -19,8 +17,13 @@ To build OSv
     cp ./src/.libs/libunwind.a ../..
     cd ../..
 
+3) build the glibc test suite
 
-2) build osv
+   cd external/glibc-testsuite
+   make
+   cd ../..
+
+3) build osv
    ---------
 
     make
diff --git a/bootfs.manifest b/bootfs.manifest
index 6bcf0ad4524ff4b6f6a8cc18f167df6e9a134ccf..9e42d5591997df0b79428dd6a65f72855dde4d8b 100644
--- a/bootfs.manifest
+++ b/bootfs.manifest
@@ -11,7 +11,9 @@
 /usr/lib/jvm/jre/lib/rhino.jar: %(jdkbase)s/jre/lib/rhino.jar
 /usr/lib/jvm/jre/lib/meta-index: %(jdkbase)s/jre/lib/meta-index
 /usr/lib/libstdc++.so.6: %(gccbase)s/usr/lib64/libstdc++.so.6
-/usr/lib/libm.so.6: /usr/lib64/libm.so.6
+/usr/lib/libm.so.6: %(glibcbase)s/lib64/libm.so.6
 /usr/lib/libgcc_s.so.1: %(gccbase)s/lib64/libgcc_s.so.1
-/usr/lib/tst-dir.so: ./tests/tst-dir.so
 /usr/lib/tst-pthread.so: ./tests/tst-pthread.so
+/tests/tst-malloc.so: ../../external/glibc-testsuite/build/debug/malloc/tst-malloc.so
+/tests/tst-getcwd.so: ../../external/glibc-testsuite/build/debug/io/tst-getcwd.so
+
diff --git a/build.mak b/build.mak
index f0c288e608e8770da2402d711e2239dbc5aee22a..b5d4af969b84bf55275b75ddf1c39c7411fd0839 100644
--- a/build.mak
+++ b/build.mak
@@ -51,12 +51,11 @@ autodepend = -MD -MT $@ -MP
 
 do-sys-includes = $(foreach inc, $(sys-includes), -isystem $(inc))
 
-tests := tests/tst-dir.so tests/tst-pthread.so
+tests := tests/tst-pthread.so
 
-tests/tst-dir.so: tests/tst-dir.o
 tests/tst-pthread.so: tests/tst-pthread.o
 
-all: loader.img loader.bin $(tests)
+all: loader.img loader.bin
 
 boot.bin: arch/x64/boot16.ld arch/x64/boot16.o
 	$(call quiet, $(LD) -o $@ -T $^, LD $@)
@@ -117,6 +116,7 @@ objects += linux.o
 objects += sched.o
 
 include $(src)/libc/build.mak
+
 objects += $(addprefix libc/, $(libc))
 
 libstdc++.a = $(shell find $(gccbase) -name libstdc++.a)
@@ -143,7 +143,8 @@ gccbase = $(src)/external/gcc.bin
 
 bootfs.bin: scripts/mkbootfs.py bootfs.manifest $(tests)
 	$(call quiet, $(src)/scripts/mkbootfs.py -o $@ -d $@.d -m $(src)/bootfs.manifest \
-		-D jdkbase=$(jdkbase) -D gccbase=$(gccbase), MKBOOTFS $@)
+		-D jdkbase=$(jdkbase) -D gccbase=$(gccbase) -D \
+		glibcbase=$(glibcbase), MKBOOTFS $@)
 
 bootfs.o: bootfs.bin
 
diff --git a/drivers/driver.cc b/drivers/driver.cc
index 87543331fc88d9dbb94f5a05482c8e7e436eda3f..abbf22450ca617a8fdd486d7489539bc2f5ac976 100644
--- a/drivers/driver.cc
+++ b/drivers/driver.cc
@@ -85,6 +85,12 @@ bool
 Driver::Init(Device* dev) {
     if (!dev) return false;
 
+    if (!earlyInitChecks()) {
+        return false;
+    }
+
+    parse_pci_config();
+
     debug(fmt("Driver:Init %x:%x") % _vid % _id);
 
     setBusMaster(true);
@@ -116,6 +122,74 @@ Driver::getFunc() {
     return _func;
 }
 
+bool Driver::parse_pci_config(void)
+{
+    // Parse capabilities
+    bool parse_ok = parse_pci_capabilities();
+
+    return parse_ok;
+}
+
+bool Driver::parse_pci_capabilities(void)
+{
+    // FIXME: check pci device type (act differently if bridge)
+    u8 capabilities_base = pci_readb(PCI_CAPABILITIES_PTR);
+    u8 off = capabilities_base;
+
+    while (off != 0) {
+        if (off > 255) {
+            return (false);
+        }
+
+        u8 capability = pci_readb(off + PCI_CAP_OFF_ID);
+        switch (capability) {
+        case PCI_CAP_MSIX:
+            _have_msix = true;
+            debug(fmt("Have MSI-X!"));
+            break;
+        }
+
+        off = pci_readb(off + PCI_CAP_OFF_NEXT);
+    }
+
+    return true;
+}
+
+bool Driver::parse_pci_msix(void)
+{
+    return true;
+}
+
+u8 Driver::pci_readb(u8 offset)
+{
+    return read_pci_config_byte(_bus, _slot, _func, offset);
+}
+
+u16 Driver::pci_readw(u8 offset)
+{
+    return read_pci_config_word(_bus, _slot, _func, offset);
+}
+
+u32 Driver::pci_readl(u8 offset)
+{
+    return read_pci_config(_bus, _slot, _func, offset);
+}
+
+void Driver::pci_writeb(u8 offset, u8 val)
+{
+    write_pci_config_byte(_bus, _slot, _func, offset, val);
+}
+
+void Driver::pci_writew(u8 offset, u16 val)
+{
+    write_pci_config_word(_bus, _slot, _func, offset, val);
+}
+
+void Driver::pci_writel(u8 offset, u32 val)
+{
+    write_pci_config(_bus, _slot, _func, offset, val);
+}
+
 void Driver::dumpConfig() const {
     debug(fmt("Driver vid:id= %x:%x") % _vid % _id);
 }
diff --git a/drivers/driver.hh b/drivers/driver.hh
index 777999a4f59798d7bf5e1153ea0a41141fe2d47a..69dd006cad74b1f76f4c462c596c12badd45c7f2 100644
--- a/drivers/driver.hh
+++ b/drivers/driver.hh
@@ -29,6 +29,8 @@ public:
     virtual void dumpConfig() const;
     virtual bool Init(Device *d);
 
+    bool parse_pci_config(void);
+
     friend std::ostream& operator <<(std::ostream& out, const Driver &d);
     struct equal {
       bool operator()(const Driver* d1, const Driver* d2) const
@@ -60,11 +62,27 @@ protected:
     bool allocateBARs();
     virtual bool earlyInitChecks();
 
+    // Parsing of extra capabilities
+    virtual bool parse_pci_capabilities(void);
+    virtual bool parse_pci_msix(void);
+
+    // Access to PCI address space
+    virtual u8 pci_readb(u8 offset);
+    virtual u16 pci_readw(u8 offset);
+    virtual u32 pci_readl(u8 offset);
+    virtual void pci_writeb(u8 offset, u8 val);
+    virtual void pci_writew(u8 offset, u16 val);
+    virtual void pci_writel(u8 offset, u32 val);
+
     u16 _id;
     u16 _vid;
     bool _present;
     u8  _bus, _slot, _func;
     Bar* _bars[6];
+
+    // MSI-X
+    bool _have_msix;
+
 };
 
 #endif
diff --git a/drivers/pci.hh b/drivers/pci.hh
index e35c289044a2cf4713ffe068eda66f02caa1ce74..9d4ad96acacac728b02addbed6e4cfcebcf54367 100644
--- a/drivers/pci.hh
+++ b/drivers/pci.hh
@@ -37,6 +37,35 @@ using processor::outl;
 	    PCI_CAPABILITIES_PTR = 0x34,
 	};
 
+	/* Capability Register Offsets */
+	enum pci_capabilities_offsets {
+	    PCI_CAP_OFF_ID      = 0x0,
+	    PCI_CAP_OFF_NEXT    = 0x1
+	};
+
+	enum pci_capabilities {
+	    PCI_CAP_PM          = 0x01,    // PCI Power Management
+	    PCI_CAP_AGP         = 0x02,    // AGP
+	    PCI_CAP_VPD         = 0x03,    // Vital Product Data
+	    PCI_CAP_SLOTID      = 0x04,    // Slot Identification
+	    PCI_CAP_MSI         = 0x05,    // Message Signaled Interrupts
+	    PCI_CAP_CHSWP       = 0x06,    // CompactPCI Hot Swap
+	    PCI_CAP_PCIX        = 0x07,    // PCI-X
+	    PCI_CAP_HT          = 0x08,    // HyperTransport
+	    PCI_CAP_VENDOR      = 0x09,    // Vendor Unique
+	    PCI_CAP_DEBUG       = 0x0a,    // Debug port
+	    PCI_CAP_CRES        = 0x0b,    // CompactPCI central resource control
+	    PCI_CAP_HOTPLUG     = 0x0c,    // PCI Hot-Plug
+	    PCI_CAP_SUBVENDOR   = 0x0d,    // PCI-PCI bridge subvendor ID
+	    PCI_CAP_AGP8X       = 0x0e,    // AGP 8x
+	    PCI_CAP_SECDEV      = 0x0f,    // Secure Device
+	    PCI_CAP_EXPRESS     = 0x10,    // PCI Express
+	    PCI_CAP_MSIX        = 0x11,    // MSI-X
+	    PCI_CAP_SATA        = 0x12,    // SATA
+	    PCI_CAP_PCIAF       = 0x13     // PCI Advanced Features
+	};
+
+
 	u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset);
     u16 read_pci_config_word(u8 bus, u8 slot, u8 func, u8 offset);
 	u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset);
diff --git a/drivers/virtio.cc b/drivers/virtio.cc
index d05f92c009328a670dbb4a63cda29391acb2952d..38e4ab660e83af240542327429fe7ed8264325a5 100644
--- a/drivers/virtio.cc
+++ b/drivers/virtio.cc
@@ -52,10 +52,6 @@ namespace virtio {
 
     bool virtio_driver::Init(Device* dev)
     {
-        if (!earlyInitChecks()) {
-            return false;
-        }
-
         if (!Driver::Init(dev)) {
             return (false);
         }
diff --git a/elf.cc b/elf.cc
index 454175fa0bc24aeedfaadc5dcf360035adb07e56..c998b74013cc4ea9b98c3edd8215f87ffc9122d3 100644
--- a/elf.cc
+++ b/elf.cc
@@ -482,7 +482,7 @@ namespace elf {
     {
         auto needed = dynamic_str_array(DT_NEEDED);
         for (auto lib : needed) {
-            _prog.add(std::string("/usr/lib") + lib);
+            _prog.add_object(std::string("/usr/lib") + lib);
         }
     }
 
@@ -528,10 +528,10 @@ namespace elf {
         _core->load_segments();
         assert(!s_program);
         s_program = this;
-        add("libc.so.6", _core.get());
-        add("ld-linux-x86-64.so.2", _core.get());
-        add("libpthread.so.0", _core.get());
-        add("libdl.so.2", _core.get());
+        set_object("libc.so.6", _core.get());
+        set_object("ld-linux-x86-64.so.2", _core.get());
+        set_object("libpthread.so.0", _core.get());
+        set_object("libdl.so.2", _core.get());
     }
 
     tls_data program::tls()
@@ -539,12 +539,12 @@ namespace elf {
         return _core->tls();
     }
 
-    void program::add(std::string name, elf_object* obj)
+    void program::set_object(std::string name, elf_object* obj)
     {
         _files[name] = obj;
     }
 
-    elf_object* program::add(std::string name)
+    elf_object* program::add_object(std::string name)
     {
         if (!_files.count(name)) {
             auto f(_fs.open(name));
diff --git a/elf.hh b/elf.hh
index 4f56e7a663b7fe72bb28176ab1148e4486f3d08f..ae6e66535f2878b5e5b51fc2da03e7a41bdd85ba 100644
--- a/elf.hh
+++ b/elf.hh
@@ -321,8 +321,7 @@ namespace elf {
     public:
         explicit program(::filesystem& fs,
                          void* base = reinterpret_cast<void*>(0x100000000000UL));
-        elf_object* add(std::string lib);
-        void add(std::string lib, elf_object* obj);
+        elf_object* add_object(std::string lib);
         symbol_module lookup(const char* symbol);
         template <typename T>
         T* lookup_function(const char* symbol);
@@ -332,6 +331,7 @@ namespace elf {
     private:
         void add_debugger_obj(elf_object* obj);
         void* do_lookup_function(const char* symbol);
+        void set_object(std::string lib, elf_object* obj);
     private:
         ::filesystem& _fs;
         void* _next_alloc;
diff --git a/external/glibc-testsuite b/external/glibc-testsuite
new file mode 160000
index 0000000000000000000000000000000000000000..1aa7453760873926164d197f4909c2513edac0cf
--- /dev/null
+++ b/external/glibc-testsuite
@@ -0,0 +1 @@
+Subproject commit 1aa7453760873926164d197f4909c2513edac0cf
diff --git a/external/glibc.bin b/external/glibc.bin
index 55853b12d5077dac5f76e164ee29e173770d255d..ef094e3a925c6097f122399d40914d6b9bcff09f 160000
--- a/external/glibc.bin
+++ b/external/glibc.bin
@@ -1 +1 @@
-Subproject commit 55853b12d5077dac5f76e164ee29e173770d255d
+Subproject commit ef094e3a925c6097f122399d40914d6b9bcff09f
diff --git a/fs/vfs/main.c b/fs/vfs/main.c
index ea0d8b13efa5951dfd56f1f8d220f408abe893bb..c6d104dc0612c1b202bd0239355791dc67017d0a 100755
--- a/fs/vfs/main.c
+++ b/fs/vfs/main.c
@@ -564,9 +564,35 @@ int __xstat64(int ver, const char *pathname, struct stat64 *st)
 char *getcwd(char *path, size_t size)
 {
 	struct task *t = main_task;
+	int len = strlen(t->t_cwd) + 1;
+	int error;
 
-	strlcpy(path, t->t_cwd, size);
-	return 0;
+	if (!path) {
+		if (!size)
+			size = len;
+		path = malloc(size);
+		if (!path) {
+			error = ENOMEM;
+			goto out_errno;
+		}
+	} else {
+		if (!size) {
+			error = EINVAL;
+			goto out_errno;
+		}
+	}
+
+	if (size < len) {
+		error = ERANGE;
+		goto out_errno;
+	}
+
+	memcpy(path, t->t_cwd, len);
+	return path;
+
+out_errno:
+	errno = error;
+	return NULL;
 }
 
 /*
@@ -850,6 +876,7 @@ void unpack_bootfs(void)
 		"/usr/lib/jvm/jre/lib",
 		"/usr/lib/jvm/jre/lib/amd64",
 		"/usr/lib/jvm/jre/lib/amd64/server",
+		"/tests",
 		NULL,
 	};
 
diff --git a/libc/build.mak b/libc/build.mak
index faaf8d38ed80a86add70cdd7f6b676b879c5df07..bbc918196bdcc294112e4291930ba8a829250028 100644
--- a/libc/build.mak
+++ b/libc/build.mak
@@ -1,4 +1,7 @@
-libc = string/strcmp.o
+libc :=
+
+libc += getopt.o
+libc += string/strcmp.o
 libc += string/strcpy.o
 libc += string/strlcpy.o
 libc += string/strlcat.o
diff --git a/libc/dlfcn.cc b/libc/dlfcn.cc
index 980128bde498eeac77cab2216b85ddc0ddbaa4fd..e04e720d189ea7e46aa48a420e5b14aaa17a9f39 100644
--- a/libc/dlfcn.cc
+++ b/libc/dlfcn.cc
@@ -5,7 +5,7 @@
 void* dlopen(const char* filename, int flags)
 {
     auto prog = elf::get_program();
-    elf::elf_object* obj = prog->add(filename);
+    elf::elf_object* obj = prog->add_object(filename);
     // FIXME: handle flags etc.
     return obj;
 }
diff --git a/libc/getopt.c b/libc/getopt.c
new file mode 100644
index 0000000000000000000000000000000000000000..b2f5778cbfc2a8a97afcad03bc422daed437505b
--- /dev/null
+++ b/libc/getopt.c
@@ -0,0 +1,516 @@
+/****************************************************************************
+
+getopt.c - Read command line options
+
+AUTHOR: Gregory Pietsch
+CREATED Fri Jan 10 21:13:05 1997
+
+DESCRIPTION:
+
+The getopt() function parses the command line arguments.  Its arguments argc
+and argv are the argument count and array as passed to the main() function
+on program invocation.  The argument optstring is a list of available option
+characters.  If such a character is followed by a colon (`:'), the option
+takes an argument, which is placed in optarg.  If such a character is
+followed by two colons, the option takes an optional argument, which is
+placed in optarg.  If the option does not take an argument, optarg is NULL.
+
+The external variable optind is the index of the next array element of argv
+to be processed; it communicates from one call to the next which element to
+process.
+
+The getopt_long() function works like getopt() except that it also accepts
+long options started by two dashes `--'.  If these take values, it is either
+in the form
+
+--arg=value
+
+ or
+
+--arg value
+
+It takes the additional arguments longopts which is a pointer to the first
+element of an array of type struct option.  The last element of the array
+has to be filled with NULL for the name field.
+
+The longind pointer points to the index of the current long option relative
+to longopts if it is non-NULL.
+
+The getopt() function returns the option character if the option was found
+successfully, `:' if there was a missing parameter for one of the options,
+`?' for an unknown option character, and EOF for the end of the option list.
+
+The getopt_long() function's return value is described in the header file.
+
+The function getopt_long_only() is identical to getopt_long(), except that a
+plus sign `+' can introduce long options as well as `--'.
+
+The following describes how to deal with options that follow non-option
+argv-elements.
+
+If the caller did not specify anything, the default is REQUIRE_ORDER if the
+environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+REQUIRE_ORDER means don't recognize them as options; stop option processing
+when the first non-option is seen.  This is what Unix does.  This mode of
+operation is selected by either setting the environment variable
+POSIXLY_CORRECT, or using `+' as the first character of the optstring
+parameter.
+
+PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
+eventually all the non-options are at the end.  This allows options to be
+given in any order, even with programs that were not written to expect this.
+
+RETURN_IN_ORDER is an option available to programs that were written to
+expect options and other argv-elements in any order and that care about the
+ordering of the two.  We describe each non-option argv-element as if it were
+the argument of an option with character code 1.  Using `-' as the first
+character of the optstring parameter selects this mode of operation.
+
+The special argument `--' forces an end of option-scanning regardless of the
+value of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause
+getopt() and friends to return EOF with optind != argc.
+
+2012-08-26: Tried to make the error handling more sus4-like. The functions
+return a colon if getopt() and friends detect a missing argument and the
+first character of shortopts/optstring starts with a colon (`:'). If getopt()
+and friends detect a missing argument and shortopts/optstring does not start
+with a colon, the function returns a question mark (`?'). If it was a missing
+argument to a short option, optopt is set to the character in question. The
+colon goes after the ordering character (`+' or `-').
+
+COPYRIGHT NOTICE AND DISCLAIMER:
+
+Copyright (C) 1997 Gregory Pietsch
+
+This file and the accompanying getopt.h header file are hereby placed in the 
+public domain without restrictions.  Just give the author credit, don't
+claim you wrote it or prevent anyone else from using it.
+
+Gregory Pietsch's current e-mail address:
+gpietsch@comcast.net
+****************************************************************************/
+
+#ifndef HAVE_GETOPT
+
+/* include files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define __need_getopt_newlib
+#include <getopt.h>
+  
+typedef struct getopt_data
+{
+  char *optarg;
+  int optind, opterr, optopt, optwhere;
+} getopt_data;
+
+/* macros */
+  
+/* macros defined by this include file */
+#define NO_ARG          	no_argument
+#define REQUIRED_ARG    	required_argument
+#define OPTIONAL_ARG    	optional_argument
+
+/* The GETOPT_DATA_INITIALIZER macro is used to initialize a statically-
+   allocated variable of type struct getopt_data.  */
+#define GETOPT_DATA_INITIALIZER	{0,0,0,0,0}
+
+/* types */
+typedef enum GETOPT_ORDERING_T
+{
+  PERMUTE,
+  RETURN_IN_ORDER,
+  REQUIRE_ORDER
+} GETOPT_ORDERING_T;
+
+/* globally-defined variables */
+char *optarg = 0;
+int optind = 0;
+int opterr = 1;
+int optopt = '?';
+
+/* static variables */
+static int optwhere = 0;
+
+/* functions */
+
+/* reverse_argv_elements:  reverses num elements starting at argv */
+static void
+reverse_argv_elements (char **argv, int num)
+{
+  int i;
+  char *tmp;
+
+  for (i = 0; i < (num >> 1); i++)
+    {
+      tmp = argv[i];
+      argv[i] = argv[num - i - 1];
+      argv[num - i - 1] = tmp;
+    }
+}
+
+/* permute: swap two blocks of argv-elements given their lengths */
+static void
+permute (char *const argv[], int len1, int len2)
+{
+  reverse_argv_elements ((char **) argv, len1);
+  reverse_argv_elements ((char **) argv, len1 + len2);
+  reverse_argv_elements ((char **) argv, len2);
+}
+
+/* is_option: is this argv-element an option or the end of the option list? */
+static int
+is_option (char *argv_element, int only)
+{
+  return ((argv_element == 0)
+	  || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
+}
+
+/* read_globals: read the values from the globals into a getopt_data 
+   structure */
+static void
+read_globals (struct getopt_data *data)
+{
+  data->optarg = optarg;
+  data->optind = optind;
+  data->opterr = opterr;
+  data->optopt = optopt;
+  data->optwhere = optwhere;
+}
+
+/* write_globals: write the values into the globals from a getopt_data
+   structure */
+static void
+write_globals (struct getopt_data *data)
+{
+  optarg = data->optarg;
+  optind = data->optind;
+  opterr = data->opterr;
+  optopt = data->optopt;
+  optwhere = data->optwhere;
+}
+
+/* getopt_internal:  the function that does all the dirty work */
+static int
+getopt_internal (int argc, char *const argv[], const char *shortopts,
+		 const struct option *longopts, int *longind, int only,
+		 struct getopt_data *data)
+{
+  GETOPT_ORDERING_T ordering = PERMUTE;
+  size_t permute_from = 0;
+  int num_nonopts = 0;
+  int optindex = 0;
+  size_t match_chars = 0;
+  char *possible_arg = 0;
+  int longopt_match = -1;
+  int has_arg = -1;
+  char *cp = 0;
+  int arg_next = 0;
+  int initial_colon = 0;
+
+  /* first, deal with silly parameters and easy stuff */
+  if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0)
+      || data->optind >= argc || argv[data->optind] == 0)
+    return EOF;
+  if (strcmp (argv[data->optind], "--") == 0)
+    {
+      data->optind++;
+      return EOF;
+    }
+
+  /* if this is our first time through */
+  if (data->optind == 0)
+    data->optind = data->optwhere = 1;
+
+  /* define ordering */
+  if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+'))
+    {
+      ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
+      shortopts++;
+    }
+  else
+    ordering = (getenv ("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE;
+
+  /* check for initial colon in shortopts */
+  if (shortopts != 0 && *shortopts == ':')
+    {
+      ++shortopts;
+      initial_colon = 1;
+    }
+
+  /*
+   * based on ordering, find our next option, if we're at the beginning of
+   * one
+   */
+  if (data->optwhere == 1)
+    {
+      switch (ordering)
+	{
+	default:		/* shouldn't happen */
+	case PERMUTE:
+	  permute_from = data->optind;
+	  num_nonopts = 0;
+	  while (!is_option (argv[data->optind], only))
+	    {
+	      data->optind++;
+	      num_nonopts++;
+	    }
+	  if (argv[data->optind] == 0)
+	    {
+	      /* no more options */
+	      data->optind = permute_from;
+	      return EOF;
+	    }
+	  else if (strcmp (argv[data->optind], "--") == 0)
+	    {
+	      /* no more options, but have to get `--' out of the way */
+	      permute (argv + permute_from, num_nonopts, 1);
+	      data->optind = permute_from + 1;
+	      return EOF;
+	    }
+	  break;
+	case RETURN_IN_ORDER:
+	  if (!is_option (argv[data->optind], only))
+	    {
+	      data->optarg = argv[data->optind++];
+	      return (data->optopt = 1);
+	    }
+	  break;
+	case REQUIRE_ORDER:
+	  if (!is_option (argv[data->optind], only))
+	    return EOF;
+	  break;
+	}
+    }
+  /* we've got an option, so parse it */
+
+  /* first, is it a long option? */
+  if (longopts != 0
+      && (memcmp (argv[data->optind], "--", 2) == 0
+	  || (only && argv[data->optind][0] == '+')) && data->optwhere == 1)
+    {
+      /* handle long options */
+      if (memcmp (argv[data->optind], "--", 2) == 0)
+	data->optwhere = 2;
+      longopt_match = -1;
+      possible_arg = strchr (argv[data->optind] + data->optwhere, '=');
+      if (possible_arg == 0)
+	{
+	  /* no =, so next argv might be arg */
+	  match_chars = strlen (argv[data->optind]);
+	  possible_arg = argv[data->optind] + match_chars;
+	  match_chars = match_chars - data->optwhere;
+	}
+      else
+	match_chars = (possible_arg - argv[data->optind]) - data->optwhere;
+      for (optindex = 0; longopts[optindex].name != 0; ++optindex)
+	{
+	  if (memcmp
+	      (argv[data->optind] + data->optwhere, longopts[optindex].name,
+	       match_chars) == 0)
+	    {
+	      /* do we have an exact match? */
+	      if (match_chars == (int) (strlen (longopts[optindex].name)))
+		{
+		  longopt_match = optindex;
+		  break;
+		}
+	      /* do any characters match? */
+	      else
+		{
+		  if (longopt_match < 0)
+		    longopt_match = optindex;
+		  else
+		    {
+		      /* we have ambiguous options */
+		      if (data->opterr)
+			printf ("%s: option `%s' is ambiguous "
+				 "(could be `--%s' or `--%s')\n",
+				 argv[0],
+				 argv[data->optind],
+				 longopts[longopt_match].name,
+				 longopts[optindex].name);
+		      return (data->optopt = '?');
+		    }
+		}
+	    }
+	}
+      if (longopt_match >= 0)
+	has_arg = longopts[longopt_match].has_arg;
+    }
+
+  /* if we didn't find a long option, is it a short option? */
+  if (longopt_match < 0 && shortopts != 0)
+    {
+      cp = strchr (shortopts, argv[data->optind][data->optwhere]);
+      if (cp == 0)
+	{
+	  /* couldn't find option in shortopts */
+	  if (data->opterr)
+	    printf (
+		     "%s: invalid option -- `-%c'\n",
+		     argv[0], argv[data->optind][data->optwhere]);
+	  data->optwhere++;
+	  if (argv[data->optind][data->optwhere] == '\0')
+	    {
+	      data->optind++;
+	      data->optwhere = 1;
+	    }
+	  return (data->optopt = '?');
+	}
+      has_arg = ((cp[1] == ':')
+		 ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
+      possible_arg = argv[data->optind] + data->optwhere + 1;
+      data->optopt = *cp;
+    }
+
+  /* get argument and reset data->optwhere */
+  arg_next = 0;
+  switch (has_arg)
+    {
+    case OPTIONAL_ARG:
+      if (*possible_arg == '=')
+	possible_arg++;
+      data->optarg = (*possible_arg != '\0') ? possible_arg : 0;
+      data->optwhere = 1;
+      break;
+    case REQUIRED_ARG:
+      if (*possible_arg == '=')
+	possible_arg++;
+      if (*possible_arg != '\0')
+	{
+	  data->optarg = possible_arg;
+	  data->optwhere = 1;
+	}
+      else if (data->optind + 1 >= argc)
+	{
+	  if (data->opterr)
+	    {
+	      printf ("%s: argument required for option `", argv[0]);
+	      if (longopt_match >= 0)
+		{
+		  printf ("--%s'\n", longopts[longopt_match].name);
+		  data->optopt = initial_colon ? ':' : '\?';
+		}
+	      else
+		{
+		  printf ("-%c'\n", *cp);
+		  data->optopt = *cp;
+		}
+	    }
+	  data->optind++;
+	  return initial_colon ? ':' : '\?';
+	}
+      else
+	{
+	  data->optarg = argv[data->optind + 1];
+	  arg_next = 1;
+	  data->optwhere = 1;
+	}
+      break;
+    default:			/* shouldn't happen */
+    case NO_ARG:
+      if (longopt_match < 0)
+	{
+	  data->optwhere++;
+	  if (argv[data->optind][data->optwhere] == '\0')
+	    data->optwhere = 1;
+	}
+      else
+	data->optwhere = 1;
+      data->optarg = 0;
+      break;
+    }
+
+  /* do we have to permute or otherwise modify data->optind? */
+  if (ordering == PERMUTE && data->optwhere == 1 && num_nonopts != 0)
+    {
+      permute (argv + permute_from, num_nonopts, 1 + arg_next);
+      data->optind = permute_from + 1 + arg_next;
+    }
+  else if (data->optwhere == 1)
+    data->optind = data->optind + 1 + arg_next;
+
+  /* finally return */
+  if (longopt_match >= 0)
+    {
+      if (longind != 0)
+	*longind = longopt_match;
+      if (longopts[longopt_match].flag != 0)
+	{
+	  *(longopts[longopt_match].flag) = longopts[longopt_match].val;
+	  return 0;
+	}
+      else
+	return longopts[longopt_match].val;
+    }
+  else
+    return data->optopt;
+}
+
+int
+getopt (int argc, char *const argv[], const char *optstring)
+{
+  struct getopt_data data;
+  int r;
+
+  read_globals (&data);
+  r = getopt_internal (argc, argv, optstring, 0, 0, 0, &data);
+  write_globals (&data);
+  return r;
+}
+
+int
+getopt_long (int argc, char *const argv[], const char *shortopts,
+	     const struct option *longopts, int *longind)
+{
+  struct getopt_data data;
+  int r;
+
+  read_globals (&data);
+  r = getopt_internal (argc, argv, shortopts, longopts, longind, 0, &data);
+  write_globals (&data);
+  return r;
+}
+
+int
+getopt_long_only (int argc, char *const argv[], const char *shortopts,
+		  const struct option *longopts, int *longind)
+{
+  struct getopt_data data;
+  int r;
+
+  read_globals (&data);
+  r = getopt_internal (argc, argv, shortopts, longopts, longind, 1, &data);
+  write_globals (&data);
+  return r;
+}
+
+int
+__getopt_r (int argc, char *const argv[], const char *optstring,
+	    struct getopt_data *data)
+{
+  return getopt_internal (argc, argv, optstring, 0, 0, 0, data);
+}
+
+int
+__getopt_long_r (int argc, char *const argv[], const char *shortopts,
+		 const struct option *longopts, int *longind,
+		 struct getopt_data *data)
+{
+  return getopt_internal (argc, argv, shortopts, longopts, longind, 0, data);
+}
+
+int
+__getopt_long_only_r (int argc, char *const argv[], const char *shortopts,
+		      const struct option *longopts, int *longind,
+		      struct getopt_data *data)
+{
+  return getopt_internal (argc, argv, shortopts, longopts, longind, 1, data);
+}
+
+#endif /* !HAVE_GETOPT */
+
+/* end of file GETOPT.C */
diff --git a/licenses/getopt.txt b/licenses/getopt.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e74d2892e97af47429a03c0869d87b410cd232d5
--- /dev/null
+++ b/licenses/getopt.txt
@@ -0,0 +1,9 @@
+
+COPYRIGHT NOTICE AND DISCLAIMER:
+
+Copyright (C) 1997 Gregory Pietsch
+
+This file and the accompanying getopt.h header file are hereby placed in the 
+public domain without restrictions.  Just give the author credit, don't
+claim you wrote it or prevent anyone else from using it.
+
diff --git a/loader.cc b/loader.cc
index c256e66521d55ddc447efe50dad8ad349af94f6c..0cae942a5fb7cb3f6386d092526c28849bc78c69 100644
--- a/loader.cc
+++ b/loader.cc
@@ -12,6 +12,8 @@
 #include "drivers/device-factory.hh"
 #include <jni.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 //#include <locale>
 
 #include "drivers/virtio-net.hh"
@@ -187,24 +189,66 @@ void test_clock_events()
     clock_event->set_callback(old_callback);
 }
 
-#define TESTSO_PATH	"/usr/lib/tst-dir.so"
-
-void load_test(elf::program& prog)
+void load_test(elf::program& prog, char *path)
 {
-    prog.add(TESTSO_PATH);
+    printf("running %s\n", path);
+
+    prog.add_object(path);
 
     auto test_main
-        = prog.lookup_function<int (void)>("test_main");
-    int ret = test_main();
+        = prog.lookup_function<int (int, const char **)>("main");
+    std::string str = "test";
+    const char *name = str.c_str();
+    int ret = test_main(1, &name);
     if (ret)
-    	debug("FAIL");
+        printf("failed.\n");
+    else
+        printf("ok.\n");
+}
+
+
+int load_tests(elf::program& prog)
+{
+#define TESTDIR		"/tests"
+    DIR *dir = opendir(TESTDIR);
+    char path[PATH_MAX];
+    struct dirent *d;
+    struct stat st;
+
+    if (!dir) {
+        perror("failed to open testdir");
+        return EXIT_FAILURE;
+    }
+
+    while ((d = readdir(dir))) {
+        if (strcmp(d->d_name, ".") == 0 ||
+            strcmp(d->d_name, "..") == 0)
+           continue;
+
+        snprintf(path, PATH_MAX, "%s/%s", TESTDIR, d->d_name);
+        if (__xstat(1, path, &st) < 0) {
+            printf("failed to stat %s\n", path);
+            continue;
+        }
+        if (!S_ISREG(st.st_mode)) {
+            printf("ignoring %s, not a regular file\n", path);
+            continue;
+        }
+        load_test(prog, path);
+    }
+    if (closedir(dir) < 0) {
+        perror("failed to close testdir");
+        return EXIT_FAILURE;
+    }
+
+    return 0;
 }
 
 #define JVM_PATH	"/usr/lib/jvm/jre/lib/amd64/server/libjvm.so"
 
 void start_jvm(elf::program& prog)
 {
-    prog.add(JVM_PATH);
+    prog.add_object(JVM_PATH);
  
     auto JNI_GetDefaultJavaVMInitArgs
         = prog.lookup_function<void (void*)>("JNI_GetDefaultJavaVMInitArgs");
@@ -241,7 +285,7 @@ void main_thread(elf::program& prog)
     debug(fmt("clock@t1 %1%") % t1);
     debug(fmt("clock@t2 %1%") % t2);
 
-//    load_test(prog);
+//    load_tests(prog);
     start_jvm(prog);
 
     while (true)
diff --git a/mempool.cc b/mempool.cc
index f445b436b0627bb5fb9e6f088053bf152875d2f6..b15555767c5d11dd228c0d2e7120c2895cd01cf6 100644
--- a/mempool.cc
+++ b/mempool.cc
@@ -6,6 +6,7 @@
 #include <new>
 #include <boost/utility.hpp>
 #include <string.h>
+#include "libc/libc.hh"
 
 namespace memory {
 
@@ -271,6 +272,9 @@ extern "C" {
 
 void* malloc(size_t size)
 {
+    if ((ssize_t)size < 0)
+        return libc_error_ptr<void *>(ENOMEM);
+    
     if (size <= memory::pool::max_object_size) {
         size = std::max(size, memory::pool::min_object_size);
         unsigned n = ilog2_roundup(size);
diff --git a/runtime.cc b/runtime.cc
index 0130f1cf41ca47ec0683226093420e81afda7b16..5e20935323f555301160757cd0da46866576a7b7 100644
--- a/runtime.cc
+++ b/runtime.cc
@@ -84,7 +84,7 @@ extern "C" {
 		       __locale_t __l) __THROW __nonnull ((2, 4));
     size_t __wcsxfrm_l(wchar_t *__s1, __const wchar_t *__s2,
 			 size_t __n, __locale_t __loc) __THROW;
-
+    int mallopt(int param, int value);
 }
 
 void *__dso_handle;
@@ -412,7 +412,11 @@ int puts(const char *s)
 UNIMPL(size_t wcslen(const wchar_t *s))
 UNIMPL(int wmemcmp(const wchar_t *s1, const wchar_t *s2, size_t n))
 UNIMPL(wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t n))
-UNIMPL(int setvbuf(FILE *stream, char *buf, int mode, size_t size))
+int setvbuf(FILE *stream, char *buf, int mode, size_t size)
+{
+    debug("stub setvbuf()");
+    return 0;
+}
 UNIMPL(size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream))
 UNIMPL(size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream))
 UNIMPL(wint_t fgetwc(FILE *stream))
@@ -620,6 +624,12 @@ size_t confstr(int name, char* buf, size_t len)
     abort();
 }
 
+int mallopt(int param, int value)
+{
+    debug(fmt("mallopt: unimplemented paramater  %1%") % param);
+    return 0;
+}
+
 long timezone;
 
 char* __environ_array[1];
diff --git a/scripts/run.sh b/scripts/run.sh
index b231732e1cc5fe1a1e356c6339f8fa881a4e15f8..dd48c88d85584b833933885bd0f9dd41d81de908 100644
--- a/scripts/run.sh
+++ b/scripts/run.sh
@@ -5,4 +5,5 @@ qemu-system-x86_64 \
 	-chardev stdio,mux=on,id=stdio \
 	-mon chardev=stdio,mode=readline,default \
 	-device isa-serial,chardev=stdio \
+	-device virtio-net-pci \
 	-drive file=build/debug/loader.img,if=virtio,cache=unsafe
diff --git a/tests/tst-dir.c b/tests/tst-dir.c
deleted file mode 100644
index 1b8df93d1b3efe602ee1829cf25982638f6b207e..0000000000000000000000000000000000000000
--- a/tests/tst-dir.c
+++ /dev/null
@@ -1,99 +0,0 @@
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-int sys_mount(char *dev, char *dir, char *fsname, int flags, void *data);
-int ll_readdir(int fd, struct dirent *d);
-
-int test_vfs(void)
-{
-	struct dirent d;
-	int fd, ret;
-	const char test[] = "Hello World! HelloHello!";
-	char buf[512];
-	ssize_t len;
-
-	fd = open("/test", O_RDWR|O_CREAT, 666);
-	if (fd < 0) {
-		printf("open returned %d", fd);
-	}
-
-	len = sizeof(test);
-	if ((ret = write(fd, test, len)) != len) {
-		printf("write failed, len %d out of %zd, errno %d\n",
-			ret, len, errno);
-	}
-
-	if (lseek(fd, 0, SEEK_SET) != 0)
-		printf("lseek returned %d", errno);
-
-	if ((ret = read(fd, buf, sizeof(buf))) < len) {
-		printf("read failed, len %d out of %ld, errno %d\n",
-			ret, sizeof(buf), errno);
-	}
-
-	printf("payload: %s\n", buf);
-
-	close(fd);
-	
-	fd = open("/", O_RDONLY, 0);
-	if (fd < 0)
-		printf("open root returned %d", fd);
-
-	while (ll_readdir(fd, &d) == 0) {
-		printf("name: %s, ino: %ld, off: %ld, reclen: %d, type %d\n",
-			d.d_name, d.d_ino, d.d_off, d.d_reclen, d.d_type);
-	}
-
-	return ret;
-}
-
-int test_main(void)
-{
-	DIR *dir = opendir("/usr");
-	struct dirent entry, *d;
-	int ret;
-
-	if (!dir) {
-		perror("failed to open /usr");
-		return EXIT_FAILURE;
-	}
-
-	printf("testing readdir:\n");
-	while ((d = readdir(dir))) {
-		printf("name: %s, ino: %ld, off: %ld, reclen: %d, type %d\n",
-			d->d_name, d->d_ino, d->d_off, d->d_reclen, d->d_type);
-	}
-
-	if (!d && errno) {
-		perror("readdir failed");
-		return EXIT_FAILURE;
-	}
-
-	printf("testing readdir_r:\n");
-	for (;;) {
-		ret = readdir_r(dir, &entry, &d);
-		if (ret) {
-			errno = ret;
-			perror("readdir_r failed");
-			return EXIT_FAILURE;
-		}
-		if (!d)
-			break;
-		printf("name: %s, ino: %ld, off: %ld, reclen: %d, type %d\n",
-			d->d_name, d->d_ino, d->d_off, d->d_reclen, d->d_type);
-	} 
-
-	if (closedir(dir) < 0) {
-		perror("failed to close /");
-		return EXIT_FAILURE;
-	}
-
-	test_vfs();
-	return 0;
-}
diff --git a/tests/tst-pthread.c b/tests/tst-pthread.c
index 63bc8619a9453e196fdcd04509702d07739c9bef..bef8bc619aa117e5f041a0cf348920cfbffd505a 100644
--- a/tests/tst-pthread.c
+++ b/tests/tst-pthread.c
@@ -84,7 +84,7 @@ void* secondary(void *ignore)
     return NULL;
 }
 
-int test_main(void)
+int main(void)
 {
     printf("starting pthread test\n");
     pthread_mutex_init(&mutex, NULL);