diff --git a/README b/README
index b2e33824ab31a29a996ada32c2dd3decc1d21361..6ade896b3ea686c0a0681092168516a291bd2ed4 100644
--- a/README
+++ b/README
@@ -2,15 +2,20 @@
 To build OSv
 ============
 
+2) make sure all git submodules are uptodate
+   -----------------------------------------
+
+    git submodule update
+
 1) build the specially patched libunwind
    -------------------------------------
 
-    cd ../libunwind
+    cd external/libunwind
     autoreconf -a
     sh config.sh
     make
-    cp ./src/.libs/libunwind.a ../osv/
-    cd ../osv
+    cp ./src/.libs/libunwind.a ../..
+    cd ../..
 
 
 2) build osv
diff --git a/bootfs.manifest b/bootfs.manifest
index 6782dcc935789245f5a8d15b3bce8e0edc015a57..68744e57786871b2b49df7c240db14ef0d82fc7b 100644
--- a/bootfs.manifest
+++ b/bootfs.manifest
@@ -6,4 +6,4 @@
 /usr/lib/libstdc++.so.6: /usr/lib64/libstdc++.so.6
 /usr/lib/libm.so.6: /usr/lib64/libm.so.6
 /usr/lib/libgcc_s.so.1: /usr/lib64/libgcc_s.so.1
-/usr/lib/hello_world.so: ./payload/hello_world.so
+/usr/lib/tst-dir.so: ./tests/tst-dir.so
diff --git a/build.mak b/build.mak
index ebbda7fbaf7070c28ad81563e31e1e739e48c20f..e076a70f4f55fa18e13fd10e0ad2b3f391fa4ea2 100644
--- a/build.mak
+++ b/build.mak
@@ -47,9 +47,9 @@ autodepend = -MD -MT $@ -MP
 
 do-sys-includes = $(foreach inc, $(sys-includes), -isystem $(inc))
 
-payload := payload/hello_world.so
+tests := tests/tst-dir.so
 
-all: loader.bin $(payload)
+all: loader.bin $(tests)
 
 loader.bin: arch/x64/boot32.o arch/x64/loader32.ld
 	$(call quiet, $(LD) -nostartfiles -static -nodefaultlibs -o $@ \
@@ -104,7 +104,7 @@ dummy-shlib.so: dummy-shlib.o
 jdkbase := $(shell find $(src)/external/openjdk.bin/usr/lib/jvm \
                          -maxdepth 1 -type d -name 'java*')
 
-bootfs.bin: scripts/mkbootfs.py bootfs.manifest $(payload)
+bootfs.bin: scripts/mkbootfs.py bootfs.manifest $(tests)
 	$(src)/scripts/mkbootfs.py -o $@ -d $@.d -m $(src)/bootfs.manifest \
 		-D jdkbase=$(jdkbase)
 
diff --git a/libc/file.cc b/libc/file.cc
index c50cc2b751660bc20476b6ca9c2b68480305813a..d46ef51c01b8c2abfe1f68e2923518cd77158719 100644
--- a/libc/file.cc
+++ b/libc/file.cc
@@ -66,3 +66,29 @@ DIR* opendir(const char* fname)
     }
     return new DIR(fd);
 }
+
+int closedir(DIR* dir)
+{
+//	::close(dir->_fd);   once we implement close
+	delete dir;
+	return 0;
+}
+
+struct dirent *readdir(DIR* dir)
+{
+	static struct dirent entry, *result;	// XXX: tls?
+	int ret;
+
+	ret = readdir_r(dir, &entry, &result);
+	if (ret)
+		return libc_error_ptr<struct dirent>(ret);
+
+	errno = 0;
+	return result;
+}
+
+int readdir_r(DIR* dir, struct dirent* entry, struct dirent** result)
+{
+	*result = NULL;
+	return 0;
+}
diff --git a/loader.cc b/loader.cc
index 215104d9acb44873effccfbf43dbbccfadba7113..db28650f7631d8f042b186297b31dae6ef2d53c7 100644
--- a/loader.cc
+++ b/loader.cc
@@ -157,15 +157,17 @@ int main(int ac, char **av)
     new thread([&] { main_thread(prog); }, true);
 }
 
-#define TESTSO_PATH	"/usr/lib/hello_world.so"
+#define TESTSO_PATH	"/usr/lib/tst-dir.so"
 
 void load_test(elf::program& prog)
 {
     prog.add(TESTSO_PATH);
 
     auto test_main
-        = prog.lookup_function<void (void)>("test_main");
-    test_main();
+        = prog.lookup_function<int (void)>("test_main");
+    int ret = test_main();
+    if (ret)
+    	debug("FAIL");
 }
 
 #define JVM_PATH	"/usr/lib/jre/lib/server/libjvm.so"
@@ -210,8 +212,8 @@ void main_thread(elf::program& prog)
     debug(fmt("clock@t1 %1%") % t1);
     debug(fmt("clock@t2 %1%") % t2);
 
+    load_test(prog);
     start_jvm(prog);
-//    load_test(prog);
 
     while (true)
 	;
diff --git a/payload/hello_world.c b/payload/hello_world.c
deleted file mode 100644
index 6fa1b5da031c0ce488600cc9df764fb1fbd8b836..0000000000000000000000000000000000000000
--- a/payload/hello_world.c
+++ /dev/null
@@ -1,7 +0,0 @@
-
-#include <stdio.h>
-
-void test_main(void)
-{
-	printf("Hello world");
-}
diff --git a/runtime.cc b/runtime.cc
index 990af44d94ebc7ef9e313785925d099723175e03..631db5d018d4c7c9c5eec3a3dbb57f9783726e8c 100644
--- a/runtime.cc
+++ b/runtime.cc
@@ -226,7 +226,17 @@ char* gettext (const char* msgid)
 
 char* strerror(int err)
 {
-    return const_cast<char*>("strerror");
+	char buf[1024];
+	char *ret = buf;
+
+	sprintf(ret, "%d", err);
+	return ret;
+}
+
+void
+perror(const char* str)
+{
+    printf("%s: %s\n", str, strerror(errno));
 }
 
 namespace __cxxabiv1 {
@@ -423,7 +433,13 @@ UNIMPL(int fputs(const char *s, FILE *stream))
 #undef putc
 UNIMPL(int putc(int c, FILE *stream))
 UNIMPL(int putchar(int c))
-UNIMPL(int puts(const char *s))
+
+int puts(const char *s)
+{
+	debug(s);
+	return 0;
+}
+
 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))
diff --git a/tests/tst-dir.c b/tests/tst-dir.c
new file mode 100644
index 0000000000000000000000000000000000000000..d47ae43096f631a1c427218be4e937b6e3b9beab
--- /dev/null
+++ b/tests/tst-dir.c
@@ -0,0 +1,50 @@
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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 %c\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 %c\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;
+	}
+
+	return 0;
+}