diff --git a/lib/module.c b/lib/module.c
index 15b465e8d964fe1a47161f0a9692498c68d0dcff..109f16ad98142b5c726505cb45ba3c1190af458b 100644
--- a/lib/module.c
+++ b/lib/module.c
@@ -1,5 +1,7 @@
 #include <stdlib.h>
 #include <dlfcn.h>
+#include <pthread.h>
+#include <unistd.h>
 
 #include "lib/defines.h"
 #include "lib/utils.h"
@@ -19,7 +21,7 @@ static inline const char *library_ext(void)
 
 static void *load_symbol(void *lib, const char *prefix, const char *name)
 {
-	auto_free char *symbol = kr_strcatdup(3, prefix, "_", name);
+	auto_free char *symbol = kr_strcatdup(2, prefix, name);
 	return dlsym(lib, symbol);
 }
 
@@ -44,6 +46,58 @@ static int load_library(struct kr_module *module, const char *name, const char *
 	return kr_error(ENOENT);
 }
 
+static int bootstrap_libgo(struct kr_module *module)
+{
+	/* Check if linked against compatible libgo */
+	void (*go_check)(void) = dlsym(module->lib, "runtime_check");
+	void (*go_args)(int, void*) = dlsym(module->lib, "runtime_args");
+	void (*go_init_os)(void) = dlsym(module->lib, "runtime_osinit");
+	void (*go_init_sched)(void) = dlsym(module->lib, "runtime_schedinit");
+	void (*go_init_main)(void) = dlsym(module->lib, "__go_init_main");
+	if ((go_check && go_args && go_init_os && go_init_sched && go_init_main) == false) {
+		return kr_error(EINVAL);
+	}
+
+	/*
+	 * Bootstrap runtime - this is minimal runtime, we would need a running scheduler
+	 * and gc for coroutines and memory allocation. That would require a custom "world loop",
+	 * message passing, and either runtime sharing or module isolation.
+	 * https://github.com/gcc-mirror/gcc/blob/gcc-4_9_2-release/libgo/runtime/proc.c#L457
+	 */
+	char *fake_argv[2] = {
+		getenv("_"),
+		NULL
+	};
+	go_check();
+	go_args(1, fake_argv);
+	go_init_os();
+	go_init_sched();
+	go_init_main();
+
+	return kr_ok();
+}
+
+
+static int load_libgo(struct kr_module *module, module_api_cb **module_api)
+{
+	/* Bootstrap libgo */
+	int ret = bootstrap_libgo(module);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* Enforced prefix for now. */
+	const char *module_prefix = "main.";
+	
+	*(void **) (module_api)      = load_symbol(module->lib, module_prefix, "Api");
+	*(void **) (&module->init)   = load_symbol(module->lib, module_prefix, "Init");
+	*(void **) (&module->deinit) = load_symbol(module->lib, module_prefix, "Deinit");
+	*(void **) (&module->config) = load_symbol(module->lib, module_prefix, "Config");
+	*(void **) (&module->layer)  = load_symbol(module->lib, module_prefix, "Layer");
+
+	return kr_ok();
+}
+
 int kr_module_load(struct kr_module *module, const char *name, const char *path)
 {
 	if (module == NULL || name == NULL) {
@@ -68,20 +122,29 @@ int kr_module_load(struct kr_module *module, const char *name, const char *path)
 
 	/* Load all symbols. */
  	module_api_cb *module_api = NULL;
-	*(void **) (&module_api)     = load_symbol(module->lib, name, "api");
-	*(void **) (&module->init)   = load_symbol(module->lib, name, "init");
-	*(void **) (&module->deinit) = load_symbol(module->lib, name, "deinit");
-	*(void **) (&module->config) = load_symbol(module->lib, name, "config");
-	*(void **) (&module->layer)  = load_symbol(module->lib, name, "layer");
+ 	auto_free char *module_prefix = kr_strcatdup(2, name, "_");
+	*(void **) (&module_api)     = load_symbol(module->lib, module_prefix, "api");
+	*(void **) (&module->init)   = load_symbol(module->lib, module_prefix, "init");
+	*(void **) (&module->deinit) = load_symbol(module->lib, module_prefix, "deinit");
+	*(void **) (&module->config) = load_symbol(module->lib, module_prefix, "config");
+	*(void **) (&module->layer)  = load_symbol(module->lib, module_prefix, "layer");
+
+	/* Module initializer not found, attempt to load as Go shared library. */
+	if (module_api == NULL) {
+		int ret = load_libgo(module, &module_api);
+		if (ret != 0) {
+			return ret;
+		}
+	}
 
 	/* Check module API version (if declared). */
-	if (module_api && module_api() > KR_MODULE_API) {
+	if (module_api && module_api() != KR_MODULE_API) {
 		return kr_error(ENOTSUP);
 	}
 
 	/* Initialize module */
 	if (module->init) {
-		return module->init(module);
+		module->init(module);
 	}
 
 	return kr_ok();
@@ -96,4 +159,4 @@ void kr_module_unload(struct kr_module *module)
 	if (module->lib && module->lib != RTLD_DEFAULT) {
 		dlclose(module->lib);
 	}
-}
\ No newline at end of file
+}
diff --git a/modules/gostats/gostats.go b/modules/gostats/gostats.go
index f4d73c162930527f77ab7de732abf5209e181900..e4aa4f188bbc9b922b48128f411d9b44528e7500 100644
--- a/modules/gostats/gostats.go
+++ b/modules/gostats/gostats.go
@@ -1,15 +1,15 @@
-package gostats
+package main
 
 /*
 #include "lib/layer.h"
 #include "lib/module.h"
-extern int gostats_begin(knot_layer_t *, void *);
-extern int gostats_finish(knot_layer_t *);
+extern int begin(knot_layer_t *, void *) __asm__ ("main.Begin");
+extern int finish(knot_layer_t *) __asm__ ("main.Finish");
 static inline const knot_layer_api_t *_gostats_layer(void)
 {
 	static const knot_layer_api_t _module = {
-		.begin = &gostats_begin,
-		.finish = &gostats_finish
+		.begin = &begin,
+		.finish = &finish
 	};
 	return &_module;
 }
@@ -18,31 +18,30 @@ import "C"
 import "unsafe"
 import "fmt"
 
-//export gostats_init
-func gostats_init(module *C.struct_kr_module) C.int {
-	fmt.Println("go_init()")
+func Api() C.uint32_t {
+	return C.KR_MODULE_API
+}
+
+func Init(module *C.struct_kr_module) C.int {
+	fmt.Printf("go_init(%s)\n", C.GoString((*C.char)(module.data)))
 	return 0
 }
 
-//export gostats_deinit
-func gostats_deinit(module *C.struct_kr_module) C.int {
+func Deinit(module *C.struct_kr_module) C.int {
 	fmt.Println("go_deinit()")
 	return 0
 }
 
-//export gostats_begin
-func gostats_begin(ctx *C.knot_layer_t, param unsafe.Pointer) C.int {
+func Begin(ctx *C.knot_layer_t, param unsafe.Pointer) C.int {
 	fmt.Println("go_begin()")
 	return 0
 }
 
-//export gostats_finish
-func gostats_finish(ctx *C.knot_layer_t) C.int {
+func Finish(ctx *C.knot_layer_t) C.int {
 	fmt.Println("go_finish()")
 	return 0
 }
 
-//export gostats_layer
-func gostats_layer() *C.knot_layer_api_t {
+func Layer() *C.knot_layer_api_t {
 	return C._gostats_layer()
 }
\ No newline at end of file
diff --git a/modules/modules.mk b/modules/modules.mk
index 7fdcace955ab001cbd58221ae639bf6138d42a14..b4fdcda4ae9b0e2be8cc571a6987b5d38d78e6a0 100644
--- a/modules/modules.mk
+++ b/modules/modules.mk
@@ -14,14 +14,16 @@ endef
 
 # Go target definition
 define go_target
-$(1): $(2)/$(1)$(LIBEXT)
-$(2)/_obj/_cgo_.o: $$($(1)_SOURCES)
-	$(INSTALL) -d $(2)/_obj
+$(1)_OBJS := $(addprefix $(2)/_obj/,_cgo_defun.o _cgo_export.o $(subst /,_,$(2))_$(1).cgo2.o)
+$(1)_GOBJS := $(addprefix $(2)/_obj/,_cgo_gotypes.go $(subst /,_,$(2))_$(1).cgo1.go)
+$(2)/_obj/_cgo_export.h: $$($(1)_SOURCES)
+	@$(INSTALL) -d $(2)/_obj
 	$(call quiet,CGO,$$^) -gccgo=true -objdir=$(2)/_obj -- $(CFLAGS) $$^
-$(2)/_obj/$(1).o: $(2)/_obj/_cgo_.o
-	$(call quiet,GCCGO,$$@) -fPIC -c $(2)/_obj/*.go
-$(2)/$(1)$(LIBEXT): $(2)/_obj/$(1).o $$($(1)_DEPEND)
-	$(call quiet,GCCGO,$$@) $(CFLAGS) -$(LIBTYPE) -fPIC -Wno-return-type -o $$@ $(2)/_obj/*.o $(2)/_obj/*.c -lgcc $$($(1)_LIBS)
+$(2)/$(1).o: $(2)/_obj/_cgo_export.h $$($(1)_GOBJS)
+	$(call quiet,GCCGO,$$@) -I$(2)/_obj -c -fPIC $$($(1)_GOBJS) -o $$@
+$(2)/$(1)$(LIBEXT): $(2)/_obj/_cgo_export.h $(2)/$(1).o $$($(1)_OBJS) $$($(1)_DEPEND)
+	$(call quiet,GCCGO,$$@) -g -fPIC -I$(2)/_obj $(2)/$(1).o $$($(1)_OBJS) -o $$@ -$(LIBTYPE) -lgcc -lgo $$($(1)_LIBS)
+$(1): $(2)/_obj/_cgo_export.h $(2)/$(1)$(LIBEXT)
 $(1)-clean:
 	$(RM) -r $(2)/_obj $(2)/$(1)$(LIBEXT)
 $(1)-install: $(2)/$(1)$(LIBEXT)