diff --git a/Makefile b/Makefile
index 62c82eb14aedee2fef964cd9fb823e50ff3357cc..ff17e1d2166b9e4026ff91454318ec1adeb3d22c 100644
--- a/Makefile
+++ b/Makefile
@@ -258,6 +258,7 @@ PERL		= perl
 PYTHON		= python
 CHECK		= sparse
 
+
 CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
 		  -Wbitwise -Wno-return-void $(CF)
 NOSTDINC_FLAGS  =
@@ -596,14 +597,43 @@ leanos-kernel	:= $(patsubst %/, %/built-in.o, $(kernel-y))
 leanos-init	:= $(patsubst %/, %/built-in.o, $(init-y))
 leanos-libs	:= $(patsubst %/, %/lib.a, $(libs-y))
 
-leanos-deps	:= $(leanos-init) $(leanos-core) $(leanos-kernel) $(leanos-libs)
+
+# Externally visible symbols (used by link-leanos.sh)
+export KBUILD_LEANOS_INIT := $(leanos-init)
+export KBUILD_LEANOS_MAIN := $(leanos-core) $(leanos-kernel) $(leanos-libs)
+export KBUILD_LDS          := arch/$(SRCARCH)/kernel/leanos.lds
+export LDFLAGS_leanos
+
+
+leanos-deps := $(KBUILD_LDS) $(KBUILD_LEANOS_INIT) $(KBUILD_LEANOS_MAIN)
 
 quiet_cmd_leanos = LD      $@
       cmd_leanos = $(CC) $(LDFLAGS) -o $@                          \
       -Wl,--start-group $(leanos-deps) -Wl,--end-group
 
-leanos: $(leanos-deps) FORCE
-	+$(call if_changed,leanos)
+
+
+PHONY += leanos_prereq
+leanos_prereq: $(leanos-deps) FORCE
+
+ifdef CONFIG_TRIM_UNUSED_KSYMS
+	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh \
+	  "$(MAKE) -f $(srctree)/Makefile leanos"
+endif
+
+# standalone target for easier testing
+include/generated/autoksyms.h: FORCE
+	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh true
+
+# Final link of vmlinux with optional arch pass after final link
+    cmd_link-leanos =                                                 \
+	$(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_leanos);
+
+
+leanos: leanos_prereq
+	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-leanos.sh
+
+
 
 # Build samples along the rest of the kernel
 ifdef CONFIG_SAMPLES
@@ -797,17 +827,20 @@ clean: rm-dirs  := $(CLEAN_DIRS)
 clean: rm-files := $(CLEAN_FILES)
 clean-dirs      := $(addprefix _clean_, . $(leanos-dirs))
 
+leanosclean:
+	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-leanos.sh clean
+
 PHONY += $(clean-dirs) clean archclean
 $(clean-dirs):
 	$(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
 
-clean: $(clean-dirs)
+clean: $(clean-dirs) leanosclean
 	$(call cmd,rmdirs)
 	$(call cmd,rmfiles)
 	@find . $(RCS_FIND_IGNORE) \
 		\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
 		-o -name '*.ko.*' \
-		-o -name '.*.d' -o -name '.*.tmp' \
+		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
 		-o -name '*.symtypes' -o -name 'modules.order' \
 		-o -name modules.builtin -o -name '.tmp_*.o.*' \
 		-o -name '*.gcno' \) -type f -print | xargs rm -f
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index d91ba92498f984fa9ac95c7165e31f4ffcb32975..48692145a056402dc1b4dc5c68fafe0d049dbba1 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -7,5 +7,7 @@ obj-y += init.o
 obj-y += page.o
 obj-y += bootmem.o
 obj-y += mm.o
+obj-y += elf.o
+obj-y += module.o
 
 #libs-y                 += lib/
diff --git a/arch/sparc/kernel/elf.c b/arch/sparc/kernel/elf.c
new file mode 100644
index 0000000000000000000000000000000000000000..f886290d2255cebca2129fbcf91739a79ab0823e
--- /dev/null
+++ b/arch/sparc/kernel/elf.c
@@ -0,0 +1,50 @@
+#include <kernel/elf.h>
+
+
+
+
+
+
+/**
+ * @brief check if the ELF file can be used by us 
+ */
+
+int elf_header_check(Elf_Ehdr *ehdr)
+{
+	if (ehdr->e_shentsize != sizeof(Elf_Shdr))
+		return -1;
+
+	if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0))
+		return -1;
+
+	if (!(ehdr->e_ident[EI_MAG1] == ELFMAG1))
+		return -1;
+
+	if (!(ehdr->e_ident[EI_MAG2] == ELFMAG2))
+		return -1;
+
+	if (!(ehdr->e_ident[EI_MAG3] == ELFMAG3))
+		return -1;
+
+	if (!(ehdr->e_ident[EI_CLASS] == ELFCLASS32))
+		if (!(ehdr->e_ident[EI_CLASS] == ELFCLASS64))
+			return -1;
+
+	if (!elf_check_endian(ehdr))
+		return -1;
+
+	if (!(ehdr->e_ident[EI_VERSION] == EV_CURRENT))
+		return -1;
+
+	/* .o files only */
+	if (!(ehdr->e_type == ET_REL))
+		return -1;
+
+	if(!elf_check_arch(ehdr))
+		return -1;
+
+	if (!(ehdr->e_version == EV_CURRENT))
+		return -1;
+
+	return 0;
+}
diff --git a/arch/sparc/kernel/leanos.lds b/arch/sparc/kernel/leanos.lds
new file mode 100644
index 0000000000000000000000000000000000000000..bb8f380f01c3b9c38636470e3116845d7a19191c
--- /dev/null
+++ b/arch/sparc/kernel/leanos.lds
@@ -0,0 +1 @@
+OUTPUT_FORMAT("elf32-sparc")
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
new file mode 100644
index 0000000000000000000000000000000000000000..7189813918a83b317bbd214e92f2ee2ad4ae270d
--- /dev/null
+++ b/arch/sparc/kernel/module.c
@@ -0,0 +1,102 @@
+#include <kernel/module.h>
+#include <kernel/printk.h>
+#include <kernel/err.h>
+
+
+/**
+ * @brief apply relocation + addend
+ *
+ * @param m an ELF module
+ * @param rel an ELF relocation entry
+ * @param sym the address of the target symbol
+ *
+ * return 0 on success
+ */
+
+int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym)
+{
+	Elf_Addr rsym;
+
+	uint8_t  *loc8;
+	uint32_t *loc32;
+
+	struct module_section *text;
+
+
+
+	if (!m)
+		return -EINVAL;
+
+	if (!rel)
+		return -EINVAL;
+
+	if (!sym)
+		return -EINVAL;
+
+
+
+	text = find_mod_sec(m, ".text");
+
+	loc8  = (uint8_t  *) (text->addr + rel->r_offset);
+	loc32 = (uint32_t *) loc8;
+
+
+	/* the symbol address it is referring to */
+	rsym = sym + rel->r_addend;
+
+
+	/* SPARC relocations are annoying, these are just the most common ones,
+	 * add as needed
+	 * http://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-54839.html
+	 */
+
+	switch (ELF_R_TYPE(rel->r_info) & 0xff) {
+	case R_SPARC_DISP32:	/* S + A - P */
+		pr_debug("\tREL type R_SPARC_DISP32\n");
+		rsym -= (Elf_Addr) loc8;
+		(*loc32) = rsym;
+		break;
+
+	case R_SPARC_32:	/* S + A */
+	case R_SPARC_UA32:	/* L + A */
+		pr_debug("\tREL type R_SPARC_(UA)32\n");
+		loc8[0] = rsym >> 24;
+		loc8[1] = rsym >> 16;
+		loc8[2] = rsym >>  8;
+		loc8[3] = rsym >>  0;
+		break;
+
+	case R_SPARC_WDISP30:	/* (S + A - P) >> 2 */
+		pr_debug("\tREL type R_SPARC_WDISP30\n");
+		rsym -= (Elf_Addr) loc8;
+		(*loc32) = ((*loc32) & ~(((1 << 30)) - 1)) |
+			((rsym >> 2) &   ((1 << 30)  - 1));
+		break;
+
+	case R_SPARC_WDISP22:
+		pr_debug("\tREL type R_SPARC_WDISP22\n");
+		rsym -= (Elf_Addr) loc8;
+		(*loc32) = ((*loc32) & ~(((1 << 22)) - 1)) |
+			((rsym >> 2) &   ((1 << 22)  - 1));
+		break;
+
+	case R_SPARC_HI22:
+		pr_debug("\tREL type R_SPARC_HI22\n");
+		(*loc32) = ((*loc32) & ~(((1 << 22)) - 1)) |
+			((rsym >> 10) &   ((1 << 22)  - 1));
+		break;
+
+	case R_SPARC_LO10:
+		pr_debug("\tREL type R_SPARC_LO10\n");
+		(*loc32) = ((*loc32) & ~(((1 << 10)) - 1)) |
+			(rsym &   ((1 << 10)  - 1));
+		break;
+
+	default:
+		pr_err("\tUnsupported relocation type: %x\n",
+		       (ELF_R_TYPE(rel->r_info) & 0xff));
+		return -ENOEXEC;
+	}
+
+	return 0;
+}
diff --git a/include/kernel/elf.h b/include/kernel/elf.h
new file mode 100644
index 0000000000000000000000000000000000000000..d51bde0380432fa61da4628f13cebaae41a5a5ec
--- /dev/null
+++ b/include/kernel/elf.h
@@ -0,0 +1,568 @@
+#ifndef _KERNEL_ELF_H_
+#define _KERNEL_ELF_H_
+
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* some ELF constants&stuff ripped from include/uapi/linux/elf.h */
+
+
+/* 32-bit base types */
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef  int32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+/* 64-bit base types */
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef  int16_t Elf64_SHalf;
+typedef uint64_t Elf64_Off;
+typedef  int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Xword;
+typedef  int64_t Elf64_Sxword;
+
+
+__extension__
+typedef struct elf32_dyn {
+	Elf32_Sword d_tag;		/* dynamic array tag */
+	union{
+		Elf32_Sword	d_val;
+		Elf32_Addr	d_ptr;	/* program virtual address */
+	};
+} Elf32_Dyn;
+
+__extension__
+typedef struct elf64_dyn {
+	Elf64_Sxword d_tag;		/* entry tag value */
+	union {
+		Elf64_Xword d_val;
+		Elf64_Addr d_ptr;
+	};
+} Elf64_Dyn;
+
+/* The following are used with relocations */
+#define ELF32_R_SYM(x)		((x) >> 8)
+#define ELF32_R_TYPE(x)		((x) & 0xff)
+
+#define ELF64_R_SYM(x)		((x) >> 32)
+#define ELF64_R_TYPE(x)		((x) & 0xffffffff)
+
+/* Dynamic Array Tags */
+#define DT_NULL		0		/* end of _DYNAMIC array */
+#define DT_NEEDED	1		/* str tbl offset of needed librart */
+#define DT_PLTRELSZ	2               /* size of relocation entries in PLT */
+#define DT_PLTGOT	3               /* address PLT/GOT */
+#define DT_HASH		4               /* address of symbol hash table */
+#define DT_STRTAB	5               /* address of string table */
+#define DT_SYMTAB	6               /* address of symbol table */
+#define DT_RELA		7               /* address of relocation table */
+#define DT_RELASZ	8               /* size of relocation table */
+#define DT_RELAENT	9               /* size of relocation entry */
+#define DT_STRSZ	10              /* size of string table */
+#define DT_SYMENT	11              /* size of symbol table entry */
+#define DT_INIT		12              /* address of initialization func. */
+#define DT_FINI		13              /* address of termination function */
+#define DT_SONAME	14              /* string tbl offset of shared obj */
+#define DT_RPATH 	15              /* str tbl offset of lib search path */
+#define DT_SYMBOLIC	16              /* start symbol search in shared obj */
+#define DT_REL	        17              /* address of reloc tbl with addends */
+#define DT_RELSZ	18              /* size of DT_REL relocation table */
+#define DT_RELENT	19              /* size of DT_REL relocation entry */
+#define DT_PLTREL	20              /* PLT referenced relocation entry */
+#define DT_DEBUG	21              /* used for debugging */
+#define DT_TEXTREL	22              /* allow reloc mod to unwritable seg */
+#define DT_JMPREL	23              /* addr of PLT's relocation entries */
+#define DT_ENCODING	32
+#define OLD_DT_LOOS	0x60000000
+#define DT_LOOS		0x6000000d
+#define DT_HIOS		0x6ffff000
+#define DT_VALRNGLO	0x6ffffd00
+#define DT_VALRNGHI	0x6ffffdff
+#define DT_ADDRRNGLO	0x6ffffe00
+#define DT_ADDRRNGHI	0x6ffffeff
+#define DT_VERSYM	0x6ffffff0
+#define DT_RELACOUNT	0x6ffffff9
+#define DT_RELCOUNT	0x6ffffffa
+#define DT_FLAGS_1	0x6ffffffb
+#define DT_VERDEF	0x6ffffffc
+#define	DT_VERDEFNUM	0x6ffffffd
+#define DT_VERNEED	0x6ffffffe
+#define	DT_VERNEEDNUM	0x6fffffff
+#define OLD_DT_HIOS     0x6fffffff
+#define DT_LOPROC	0x70000000
+#define DT_HIPROC	0x7fffffff
+
+
+/* Dynamic Flags - DT_FLAGS_1 .dynamic entry */
+#define DF_1_NOW       0x00000001
+#define DF_1_GLOBAL    0x00000002
+#define DF_1_GROUP     0x00000004
+#define DF_1_NODELETE  0x00000008
+#define DF_1_LOADFLTR  0x00000010
+#define DF_1_INITFIRST 0x00000020
+#define DF_1_NOOPEN    0x00000040
+#define DF_1_ORIGIN    0x00000080
+#define DF_1_DIRECT    0x00000100
+#define DF_1_TRANS     0x00000200
+#define DF_1_INTERPOSE 0x00000400
+#define DF_1_NODEFLIB  0x00000800
+#define DF_1_NODUMP    0x00001000
+#define DF_1_CONLFAT   0x00002000
+
+/* ld.so: number of low tags that are used saved internally (0 .. DT_NUM-1) */
+#define DT_NUM        (DT_JMPREL+1)
+
+/* This info is needed when parsing the symbol table */
+#define STB_LOCAL  0
+#define STB_GLOBAL 1
+#define STB_WEAK   2
+
+#define STT_NOTYPE  0
+#define STT_OBJECT  1
+#define STT_FUNC    2
+#define STT_SECTION 3
+#define STT_FILE    4
+#define STT_COMMON  5
+#define STT_TLS     6
+
+#define ELF_ST_BIND(x)		((x) >> 4)
+#define ELF_ST_TYPE(x)		(((unsigned int) x) & 0xf)
+#define ELF32_ST_BIND(x)	ELF_ST_BIND(x)
+#define ELF32_ST_TYPE(x)	ELF_ST_TYPE(x)
+#define ELF64_ST_BIND(x)	ELF_ST_BIND(x)
+#define ELF64_ST_TYPE(x)	ELF_ST_TYPE(x)
+
+
+
+typedef struct elf32_rel {
+	Elf32_Addr	r_offset;
+	Elf32_Word	r_info;
+} Elf32_Rel;
+
+typedef struct elf64_rel {
+	Elf64_Addr r_offset;	/* Location at which to apply the action */
+	Elf64_Xword r_info;	/* index and type of relocation */
+} Elf64_Rel;
+
+typedef struct elf32_rela{
+	Elf32_Addr	r_offset;
+	Elf32_Word	r_info;
+	Elf32_Sword	r_addend;
+} Elf32_Rela;
+
+typedef struct elf64_rela {
+	Elf64_Addr r_offset;	/* Location at which to apply the action */
+	Elf64_Xword r_info;	/* index and type of relocation */
+	Elf64_Sxword r_addend;	/* Constant addend used to compute value */
+} Elf64_Rela;
+
+
+/* relocs as needed */
+#define R_AMD64_NONE     0
+#define R_AMD64_RELATIVE 8
+
+
+
+
+typedef struct elf32_sym{
+	Elf32_Word	st_name;
+	Elf32_Addr	st_value;
+	Elf32_Word	st_size;
+	unsigned char	st_info;
+	unsigned char	st_other;
+	Elf32_Half	st_shndx;
+} Elf32_Sym;
+
+typedef struct elf64_sym {
+	Elf64_Word	st_name;	/* Symbol name, index in string tbl */
+	unsigned char	st_info;	/* Type and binding attributes */
+	unsigned char	st_other;	/* No defined meaning, 0 */
+	Elf64_Half	st_shndx;	/* Associated section index */
+	Elf64_Addr	st_value;	/* Value of the symbol */
+	Elf64_Xword	st_size;	/* Associated symbol size */
+} Elf64_Sym;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Note Definitions
+ */
+typedef struct {
+    Elf32_Word namesz;
+    Elf32_Word descsz;
+    Elf32_Word type;
+} Elf32_Note;
+
+typedef struct {
+    Elf64_Half namesz;
+    Elf64_Half descsz;
+    Elf64_Half type;
+} Elf64_Note;
+
+
+
+
+
+/**
+ * e_ident[] identification indexes
+ */
+
+#define EI_MAG0		0	/* file ID */
+#define EI_MAG1		1	/* file ID */
+#define EI_MAG2		2	/* file ID */
+#define EI_MAG3		3	/* file ID */
+#define EI_CLASS	4	/* file class */
+#define EI_DATA		5	/* data encoding */
+#define EI_VERSION	6	/* ELF header version */
+#define EI_OSABI	7	/* OS/ABI ID */
+#define EI_ABIVERSION	8	/* ABI version */
+#define EI_PAD		9	/* start of pad bytes */
+#define EI_NIDENT	16	/* size of e_ident[] */
+
+/* e_ident[] magic numbers */
+#define ELFMAG0		0x7f		/* e_ident[EI_MAG0] */
+#define ELFMAG1		'E'		/* e_ident[EI_MAG1] */
+#define ELFMAG2		'L'		/* e_ident[EI_MAG2] */
+#define ELFMAG3		'F'		/* e_ident[EI_MAG3] */
+#define ELFMAG		"\177ELF"	/* magic */
+#define SELFMAG		4		/* size of magic */
+
+/* e_ident[] file classes */
+#define ELFCLASSNONE	0	/* invalid */
+#define ELFCLASS32	1	/* 32-bit objs */
+#define ELFCLASS64	2	/* 64-bit objs */
+#define ELFCLASSNUM	3	/* number of classes */
+
+/* e_ident[] data encoding */
+#define ELFDATANONE	0	/* invalid */
+#define ELFDATA2LSB	1	/* Little-Endian */
+#define ELFDATA2MSB	2	/* Big-Endian */
+#define ELFDATANUM	3	/* number of data encode defines */
+
+/* e_ident[] Operating System/ABI */
+#define ELFOSABI_SYSV		0	/* UNIX System V ABI */
+#define ELFOSABI_HPUX		1	/* HP-UX operating system */
+#define ELFOSABI_NETBSD		2	/* NetBSD */
+#define ELFOSABI_LINUX		3	/* GNU/Linux */
+#define ELFOSABI_HURD		4	/* GNU/Hurd */
+#define ELFOSABI_86OPEN		5	/* 86Open common IA32 ABI */
+#define ELFOSABI_SOLARIS	6	/* Solaris */
+#define ELFOSABI_MONTEREY	7	/* Monterey */
+#define ELFOSABI_IRIX		8	/* IRIX */
+#define ELFOSABI_FREEBSD	9	/* FreeBSD */
+#define ELFOSABI_TRU64		10	/* TRU64 UNIX */
+#define ELFOSABI_MODESTO	11	/* Novell Modesto */
+#define ELFOSABI_OPENBSD	12	/* OpenBSD */
+#define ELFOSABI_ARM		97	/* ARM */
+#define ELFOSABI_STANDALONE	255	/* Standalone (embedded) application */
+
+
+/* e_version */
+#define EV_NONE		0
+#define EV_CURRENT	1
+#define EV_NUM		2	/* number of versions */
+
+/* e_type */
+#define ET_NONE		0
+#define ET_REL		1	/* relocatable */
+#define ET_EXEC		2	/* executable */
+#define ET_DYN		3	/* shared object*/
+#define ET_CORE		4	/* core file */
+#define ET_NUM		5	/* number of types */
+#define ET_LOPROC	0xff00	/* reserved range for processor */
+#define ET_HIPROC	0xffff	/*  specific e_type */
+
+/* e_machine */
+#define EM_NONE		0
+#define EM_M32		1	/* AT&T WE 32100 */
+#define EM_SPARC	2	/* SPARC */
+#define EM_386		3	/* Intel 80386 */
+#define EM_68K		4	/* Motorola 68000 */
+#define EM_88K		5	/* Motorola 88000 */
+#define EM_486		6	/* Intel 80486 - unused? */
+#define EM_860		7	/* Intel 80860 */
+#define EM_MIPS		8	/* MIPS R3000 Big-Endian only */
+#define EM_MIPS_RS4_BE	10	/* MIPS R4000 Big-Endian */
+#define EM_SPARC64	11	/* SPARC v9 64-bit unofficial */
+#define EM_PARISC	15	/* HPPA */
+#define EM_SPARC32PLUS	18	/* Enhanced instruction set SPARC */
+#define EM_PPC		20	/* PowerPC */
+#define EM_ARM		40	/* ARM AArch32 */
+#define EM_ALPHA	41	/* DEC ALPHA */
+#define EM_SH		42	/* Hitachi/Renesas Super-H */
+#define EM_SPARCV9	43	/* SPARC version 9 */
+#define EM_IA_64	50	/* Intel IA-64 Processor */
+#define EM_AMD64	62	/* AMD64 architecture */
+#define EM_VAX		75	/* DEC VAX */
+#define EM_AARCH64	183	/* ARM AArch64 */
+
+
+/* ELF32 Header */
+typedef struct elf32_hdr{
+	unsigned char	e_ident[EI_NIDENT];	/* ELF identifier */
+	Elf32_Half	e_type;			/* object file type */
+	Elf32_Half	e_machine;		/* machine type */
+	Elf32_Word	e_version;		/* object file version */
+	Elf32_Addr	e_entry;		/* entry point */
+	Elf32_Off	e_phoff;		/* program header offset */
+	Elf32_Off	e_shoff;		/* section header offset */
+	Elf32_Word	e_flags;		/* processor-specific flags */
+	Elf32_Half	e_ehsize;		/* ELF header size */
+	Elf32_Half	e_phentsize;		/* program header entry size */
+	Elf32_Half	e_phnum;		/* number of program headers */
+	Elf32_Half	e_shentsize;		/* section header entry size */
+	Elf32_Half	e_shnum;		/* number of section headers */
+	Elf32_Half	e_shstrndx;		/* string table index */
+} Elf32_Ehdr;
+
+/* ELF64 Header */
+typedef struct elf64_hdr{
+	unsigned char	e_ident[EI_NIDENT];	/* ELF identifier */
+	Elf64_Half	e_type;			/* object file type */
+	Elf64_Half	e_machine;		/* machine type */
+	Elf64_Word	e_version;		/* object file version */
+	Elf64_Addr	e_entry;		/* entry point */
+	Elf64_Off	e_phoff;		/* program header offset */
+	Elf64_Off	e_shoff;		/* section header offset */
+	Elf64_Word	e_flags;		/* processor-specific flags */
+	Elf64_Half	e_ehsize;		/* ELF header size */
+	Elf64_Half	e_phentsize;		/* program header entry size */
+	Elf64_Half	e_phnum;		/* number of program headers */
+	Elf64_Half	e_shentsize;		/* section header entry size */
+	Elf64_Half	e_shnum;		/* number of section headers */
+	Elf64_Half	e_shstrndx;		/* string table index */
+} Elf64_Ehdr;
+
+
+/* ELF32 Program Header */
+typedef struct elf32_phdr{
+	Elf32_Word	p_type;			/* entry type */
+	Elf32_Off	p_flags;		/* flags */
+	Elf32_Addr	p_offset;		/* file offset */
+	Elf32_Addr	p_vaddr;		/* virtual address */
+	Elf32_Word	p_paddr;		/* physical address */
+	Elf32_Word	p_filesz;		/* size in file */
+	Elf32_Word	p_memsz;		/* size in memory */
+	Elf32_Word	p_align;		/* alignment, file & memory */
+} Elf32_Phdr;
+
+/* ELF64 Program Header */
+typedef struct elf64_phdr {
+	Elf64_Word	p_type;			/* entry type */
+	Elf64_Word	p_flags;		/* flags */
+	Elf64_Off	p_offset;		/* file offset */
+	Elf64_Addr	p_vaddr;		/* virtual address */
+	Elf64_Addr	p_paddr;		/* physical address */
+	Elf64_Xword	p_filesz;		/* size in file */
+	Elf64_Xword	p_memsz;		/* size in memory */
+	Elf64_Xword	p_align;		/* alignment, file & memory */
+} Elf64_Phdr;
+
+
+/* Program entry types */
+#define PT_NULL		0		/* unused */
+#define PT_LOAD		1		/* loadable segment */
+#define PT_DYNAMIC	2		/* dynamic linking section */
+#define PT_INTERP	3		/* the RTLD */
+#define PT_NOTE		4		/* auxiliary information */
+#define PT_SHLIB	5		/* reserved - purpose undefined */
+#define PT_PHDR		6		/* program header */
+#define PT_TLS		7		/* thread local storage */
+#define PT_LOOS		0x60000000	/* reserved range for OS */
+#define PT_HIOS		0x6fffffff	/*  specific segment types */
+#define PT_LOPROC	0x70000000	/* reserved range for processor */
+#define PT_HIPROC	0x7fffffff	/*  specific segment types */
+
+
+/* ELF32 Section Header */
+typedef struct elf32_shdr {
+	Elf32_Word	sh_name;		/* section name index */
+	Elf32_Word	sh_type;		/* section type */
+	Elf32_Word	sh_flags;		/* section flags */
+	Elf32_Addr	sh_addr;		/* (virtual) address */
+	Elf32_Off	sh_offset;		/* file offset */
+	Elf32_Word	sh_size;		/* section size */
+	Elf32_Word	sh_link;		/* link to other section header */
+	Elf32_Word	sh_info;		/* misc info */
+	Elf32_Word	sh_addralign;		/* memory address alignment */
+	Elf32_Word	sh_entsize;		/* section entry size */
+} Elf32_Shdr;
+
+/* ELF64 Section Header */
+typedef struct elf64_shdr {
+	Elf64_Word	sh_name;		/* section name */
+	Elf64_Word	sh_type;		/* section type */
+	Elf64_Xword	sh_flags;		/* section flags */
+	Elf64_Addr	sh_addr;		/* (virtual) address */
+	Elf64_Off	sh_offset;		/* file offset */
+	Elf64_Xword	sh_size;		/* section size */
+	Elf64_Word	sh_link;		/* link to other section header */
+	Elf64_Word	sh_info;		/* misc info */
+	Elf64_Xword	sh_addralign;		/* memory address alignment */
+	Elf64_Xword	sh_entsize;		/* section entry size */
+} Elf64_Shdr;
+
+
+/* Special Section Indexes */
+#define SHN_UNDEF     0             /* undefined */
+#define SHN_LORESERVE 0xff00        /* lower bounds of reserved indexes */
+#define SHN_LOPROC    0xff00        /* reserved range for processor */
+#define SHN_HIPROC    0xff1f        /*   specific section indexes */
+#define SHN_ABS       0xfff1        /* absolute value */
+#define SHN_COMMON    0xfff2        /* common symbol */
+#define SHN_HIRESERVE 0xffff        /* upper bounds of reserved indexes */
+
+/* sh_type */
+#define SHT_NULL	0		/* inactive */
+#define SHT_PROGBITS	1		/* program defined information */
+#define SHT_SYMTAB	2		/* symbol table section */
+#define SHT_STRTAB	3		/* string table section */
+#define SHT_RELA	4		/* relocation section with addends*/
+#define SHT_HASH	5		/* symbol hash table section */
+#define SHT_DYNAMIC	6		/* dynamic section */
+#define SHT_NOTE	7		/* note section */
+#define SHT_NOBITS	8		/* no space section */
+#define SHT_REL		9		/* relation section without addends */
+#define SHT_SHLIB	10		/* reserved */
+#define SHT_DYNSYM	11		/* dynamic symbol table section */
+#define SHT_NUM		12		/* number of section types */
+#define SHT_LOPROC	0x70000000	/* reserved range for processor */
+#define SHT_HIPROC	0x7fffffff	/*  specific section header types */
+#define SHT_LOUSER	0x80000000	/* reserved range for application */
+#define SHT_HIUSER	0xffffffff	/*  specific indexes */
+
+/* Section names */
+#define ELF_BSS         ".bss"        /* uninitialized data */
+#define ELF_DATA        ".data"       /* initialized data */
+#define ELF_DEBUG       ".debug"      /* debug */
+#define ELF_DYNAMIC     ".dynamic"    /* dynamic linking information */
+#define ELF_DYNSTR      ".dynstr"     /* dynamic string table */
+#define ELF_DYNSYM      ".dynsym"     /* dynamic symbol table */
+#define ELF_FINI        ".fini"       /* termination code */
+#define ELF_GOT         ".got"        /* global offset table */
+#define ELF_HASH        ".hash"       /* symbol hash table */
+#define ELF_INIT        ".init"       /* initialization code */
+#define ELF_REL_DATA    ".rel.data"   /* relocation data */
+#define ELF_REL_FINI    ".rel.fini"   /* relocation termination code */
+#define ELF_REL_INIT    ".rel.init"   /* relocation initialization code */
+#define ELF_REL_DYN     ".rel.dyn"    /* relocation dynamic link info */
+#define ELF_REL_RODATA  ".rel.rodata" /* relocation read-only data */
+#define ELF_REL_TEXT    ".rel.text"   /* relocation code */
+#define ELF_RODATA      ".rodata"     /* read-only data */
+#define ELF_SHSTRTAB    ".shstrtab"   /* section header string table */
+#define ELF_STRTAB      ".strtab"     /* string table */
+#define ELF_SYMTAB      ".symtab"     /* symbol table */
+#define ELF_TEXT        ".text"       /* code */
+
+
+/* Section Attribute Flags - sh_flags */
+#define SHF_WRITE        0x1           /* Writable */
+#define SHF_ALLOC        0x2           /* occupies memory */
+#define SHF_EXECINSTR    0x4           /* executable */
+#define SHF_TLS          0x400         /* thread local storage */
+#define SHF_MASKPROC     0xf0000000    /* reserved bits for processor
+                                        *  specific section attributes */
+
+
+/* Sparc ELF relocation types */
+#define	R_SPARC_NONE		0
+#define	R_SPARC_8		1
+#define	R_SPARC_16		2
+#define	R_SPARC_32		3
+#define	R_SPARC_DISP8		4
+#define	R_SPARC_DISP16		5
+#define	R_SPARC_DISP32		6
+#define	R_SPARC_WDISP30		7
+#define	R_SPARC_WDISP22		8
+#define	R_SPARC_HI22		9
+#define	R_SPARC_22		10
+#define	R_SPARC_13		11
+#define	R_SPARC_LO10		12
+#define	R_SPARC_GOT10		13
+#define	R_SPARC_GOT13		14
+#define	R_SPARC_GOT22		15
+#define	R_SPARC_PC10		16
+#define	R_SPARC_PC22		17
+#define	R_SPARC_WPLT30		18
+#define	R_SPARC_COPY		19
+#define	R_SPARC_GLOB_DAT	20
+#define	R_SPARC_JMP_SLOT	21
+#define	R_SPARC_RELATIVE	22
+#define	R_SPARC_UA32		23
+#define R_SPARC_PLT32		24
+#define R_SPARC_HIPLT22		25
+#define R_SPARC_LOPLT10		26
+#define R_SPARC_PCPLT32		27
+#define R_SPARC_PCPLT22		28
+#define R_SPARC_PCPLT10		29
+#define R_SPARC_10		30
+#define R_SPARC_11		31
+#define R_SPARC_64		32
+#define R_SPARC_OLO10		33
+#define R_SPARC_WDISP16		40
+#define R_SPARC_WDISP19		41
+#define R_SPARC_7		43
+#define R_SPARC_5		44
+#define R_SPARC_6		45
+
+
+
+
+/* end of ELF stuff */
+
+
+
+/* begin configurable section */
+
+typedef Elf32_Ehdr Elf_Ehdr;
+typedef Elf32_Shdr Elf_Shdr;
+typedef Elf32_Phdr Elf_Phdr;
+typedef Elf32_Dyn  Elf_Dyn;
+typedef Elf32_Rel  Elf_Rel;
+typedef Elf32_Rela Elf_Rela;
+typedef Elf32_Off  Elf_Off;
+typedef Elf32_Addr Elf_Addr;
+typedef Elf32_Sym  Elf_Sym;
+
+#define ELF_R_SYM(x)    ELF32_R_SYM(x)
+#define ELF_R_TYPE(x)   ELF32_R_TYPE(x)
+
+
+
+/**
+ * NOTE: no free configuration, limited to 32bit ELF, SPARCv8, big endian,
+ * because I am lazy :)
+ */
+#if 1
+#define ELF_ENDIAN	ELFDATA2MSB
+#define EM_MACHINE	EM_SPARC
+#else
+#define ELF_ENDIAN	ELFDATA2LSB
+#define EM_MACHINE	EM_AMD64
+#endif
+
+
+#define elf_check_arch(x) ((x)->e_machine == EM_MACHINE)
+#define elf_check_endian(x) ((x)->e_ident[EI_DATA] == ELF_ENDIAN)
+
+
+/* implemented in arch code */
+int elf_header_check(Elf_Ehdr *ehdr);
+
+
+#endif /* _KERNEL_ELF_H_ */
diff --git a/include/kernel/ksym.h b/include/kernel/ksym.h
new file mode 100644
index 0000000000000000000000000000000000000000..bcb8e7398f1f74b1e7bce95f4cea95deb17ef401
--- /dev/null
+++ b/include/kernel/ksym.h
@@ -0,0 +1,15 @@
+#ifndef _KERNEL_KSYM_H_
+#define _KERNEL_KSYM_H_
+
+
+
+struct ksym {
+	char *name;
+	void *addr;
+};
+
+extern struct ksym __ksyms[];
+
+void *lookup_symbol(const char *name);
+
+#endif /* _KERNEL_KSYM_H_ */
diff --git a/include/kernel/module.h b/include/kernel/module.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2d7ff629d4cbc8a93b7b0766aea87f00ecb4407
--- /dev/null
+++ b/include/kernel/module.h
@@ -0,0 +1,50 @@
+#ifndef _KERNEL_MODULE_H_
+#define _KERNEL_MODULE_H_
+
+
+#include <kernel/elf.h>
+
+
+struct module_section {
+	char *name;
+	unsigned long addr;
+	size_t size;
+};
+
+
+struct elf_module {
+
+	unsigned long pa;
+	unsigned long va;
+
+
+	unsigned int align;
+
+	Elf_Ehdr *ehdr;		/* coincides with start of module image */
+	Elf_Shdr *shdr;
+	Elf_Dyn  *dyn;
+
+	size_t size;
+	size_t dyn_size;
+	size_t sh_size;
+	size_t str_size;
+
+	char  *dyn_str;		/* dynamic symbols string tab */
+	char  *sh_str;		/* section header string tab */
+	char  *str;		/* stringtab */
+
+	struct module_section *sec;
+	size_t num_sec;
+};
+
+
+
+/* implemented in architecture code */
+int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym);
+
+
+
+struct module_section *find_mod_sec(const struct elf_module *m,
+				    const char *name);
+
+#endif /* _KERNEL_MODULE_H_ */
diff --git a/init/Kconfig b/init/Kconfig
index 0c6330919084c7337816283c4327555e4101b2cd..5cd3ad80058f6e9d30e90633f538e6ab30631121 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -10,6 +10,16 @@ config KERNELVERSION
 
 menu "General Setup"
 
+config KALLSYMS
+	bool "Generate a kernel symbol table"
+	default y
+	help
+	  Create a kernel image symbol lookup table for use in loadable modules.
+	  Note: If disabled, modules can still export symbols for other modules
+	  to use, but no kernel functions will be resolvable.	
+	  If unsure, say Y.
+
+
 config CROSS_PLATFORM_TARGET
 	bool "Cross-compile for different platform"
 	default y
@@ -34,6 +44,7 @@ config TARGET_COMPILER_BOOT_CODE
 
 endmenu # "General Setup"
 
+
 menuconfig MODULES
 	bool "Enable loadable module support"
 	option modules
diff --git a/init/main.c b/init/main.c
index 0cf06ba4b8122c9a652f6788c8cb2e8998a19395..6004c42a810378a4d7d26df48dc490a35e1cc91b 100644
--- a/init/main.c
+++ b/init/main.c
@@ -6,6 +6,14 @@
 
 #include <kernel/init.h>
 
+#include <kernel/module.h> /* module_load */
+
+#include <kernel/ksym.h> /* lookup_symbol */
+
+#include <kernel/printk.h>
+
+
+int module_load(struct elf_module *m, void *p);
 
 static void kernel_init(void)
 {
@@ -17,8 +25,18 @@ static void kernel_init(void)
 
 int main(void)
 {
+
+	struct elf_module m;
+
 	kernel_init();
 
+	printk("%s at %p\n", "printk", lookup_symbol("printk"));
+	printk("%s at %p\n", "printf", lookup_symbol("printf"));
+
+
+	module_load(&m, (char *) 0xA0000000);
+	/*  load -binary kernel/test.ko 0xA0000000 */
+
 	return 0;
 }
 
diff --git a/kernel/Makefile b/kernel/Makefile
index fdb120a68f61faa27947be87b621445c16df00de..a0d25eacf0a7bf304ba1e64229a2171bc484e981 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -1,2 +1,5 @@
+obj-y += ksym.o
 obj-y += printk.o
 obj-y += bitmap.o
+obj-y += module.o
+obj-m += testmodule.o
diff --git a/kernel/ksym.c b/kernel/ksym.c
new file mode 100644
index 0000000000000000000000000000000000000000..c5a60d1626270e43e3989df1e7842e9e5e724f24
--- /dev/null
+++ b/kernel/ksym.c
@@ -0,0 +1,22 @@
+#include <string.h>
+
+#include <kernel/ksym.h>
+
+
+
+/* global kernel symbol table, filled at compile time */
+struct ksym __ksyms[] __attribute__((weak)) = {{NULL, NULL}};
+
+
+void *lookup_symbol(const char *name)
+{
+	struct ksym *s = &__ksyms[0];
+
+	while (s->name) {
+		if(!strcmp(s->name, name))
+			return s->addr;
+		s++;
+	}
+
+	return NULL;
+}
diff --git a/kernel/module.c b/kernel/module.c
new file mode 100644
index 0000000000000000000000000000000000000000..96c95d9202552485da2f7cc28061f29015668a90
--- /dev/null
+++ b/kernel/module.c
@@ -0,0 +1,936 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <kernel/printk.h>
+#include <kernel/err.h>
+#include <kernel/module.h>
+#include <kernel/ksym.h>
+
+
+/* XXX quick and dirty malloc() standin */
+#include <page.h>
+void *malloc(size_t size) {
+	return page_alloc();
+}
+
+
+/**
+ * @brief find section headers marked ALLOC by index
+ *
+ * @param offset a offset into the table (to locate multiple identical types)
+ *
+ * @return section index or 0 if not found
+ */
+
+static unsigned int find_shdr_alloc(const struct elf_module *m,
+				    const unsigned int offset)
+{
+	size_t i;
+
+
+	for (i = offset; i < m->ehdr->e_shnum; i++) {
+		if ((m->shdr[i].sh_flags & SHF_ALLOC))
+			return i;
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * @brief get number of sections marked ALLOC with size > 0
+ *
+ * @return number of ALLOC sections
+ */
+
+static unsigned int get_num_alloc_sections(const struct elf_module *m)
+{
+	size_t i;
+	size_t cnt = 0;
+
+
+	for (i = 0; i < m->ehdr->e_shnum; i++) {
+		if ((m->shdr[i].sh_flags & SHF_ALLOC))
+			if (m->shdr[i].sh_size)
+				cnt++;
+	}
+
+	return cnt;
+}
+
+
+/**
+ * @brief get the number of entries in a dynamic section
+ *
+ * @return the number of entries in a dynamic section
+ */
+
+static unsigned int get_num_dyn_entries(const struct elf_module *m)
+{
+	return m->dyn_size / sizeof(Elf_Dyn);
+}
+
+/**
+ * @brief find a dynamic entry by tag
+ *
+ * @param offset a offset into the table (to locate multiple identical types)
+ *
+ * @return NULL if entry not found, pointer to entry otherwise
+ *
+ */
+__attribute__((unused))
+static Elf_Dyn *find_dyn(const struct elf_module *m,
+			 const typeof(m->dyn->d_tag) d_tag,
+			 const unsigned int offset)
+{
+	size_t i;
+
+
+	for (i = offset; i < get_num_dyn_entries(m); i++)
+		if (d_tag == m->dyn[i].d_tag)
+			return &m->dyn[i];
+
+	return NULL;
+}
+
+
+/**
+ * @brief return a section header by index
+ *
+ * @return NULL if entry not found, pointer to entry otherwise
+ *
+ */
+
+static Elf_Shdr *get_sec(const struct elf_module *m, unsigned int idx)
+{
+	if (idx < m->ehdr->e_shnum)
+		return &m->shdr[idx];
+
+	return NULL;
+}
+
+
+/**
+ * @brief find find an elf section by type
+ *
+ * @param offset a offset into the table (to locate multiple identical types)
+ *
+ * @return section index or 0 if not found
+ *
+ */
+
+static unsigned int find_sec_type(const struct elf_module *m,
+				  const typeof(m->shdr->sh_type) sh_type,
+				  const unsigned int offset)
+{
+	size_t i;
+
+
+	for (i = offset; i < m->ehdr->e_shnum; i++) {
+		if (sh_type == m->shdr[i].sh_type)
+			return i;
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * @brief find an elf section by name
+ *
+ * @return section index or 0 if not found
+ */
+
+static unsigned int find_sec(const struct elf_module *m, const char *name)
+{
+	size_t i;
+
+
+	for (i = 1; i < m->ehdr->e_shnum; i++) {
+		if (!strcmp(m->sh_str + m->shdr[i].sh_name, name))
+			return i;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief get an entry in the .shstrtab
+ *
+ * @return pointer to the start of the string or NULL if not found
+ */
+
+static char *get_shstrtab_str(const struct elf_module *m, unsigned int idx)
+{
+	if (idx < m->sh_size)
+		return (m->sh_str + m->shdr[idx].sh_name);
+
+	return NULL;
+}
+
+/**
+ * @brief get an entry in the .strtab
+ *
+ * @return pointer to the start of the string or NULL if not found
+ */
+
+__attribute__((unused))
+static char *get_strtab_str(const struct elf_module *m, unsigned int idx)
+{
+	if (idx < m->str_size)
+		return (m->str + idx);
+
+	return NULL;
+}
+
+	
+/**
+ * @brief get the name of a symbol in .symtab with a given index
+ *
+ * @return pointer to the start of the string or NULL if not found
+ */
+
+static char *get_symbol_str(const struct elf_module *m, unsigned int idx)
+{
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+
+	symtab = &m->shdr[find_sec(m, ".symtab")];
+
+	//symbols = (Elf_Sym *) (m->pa + symtab->sh_offset);
+	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
+	
+	if (idx < symtab->sh_size / symtab->sh_entsize)
+		return m->str + symbols[idx].st_name;
+	
+	return NULL;
+}
+
+
+
+/**
+ * @brief find module section by name
+ *
+ * @return module section structure pointer or NULL if not found 
+ */
+
+struct module_section *find_mod_sec(const struct elf_module *m,
+				    const char *name) 
+
+{
+	size_t i;
+
+
+	for (i = 0; i < m->num_sec; i++) {
+		if (!strcmp(m->sec[i].name, name))
+			return &m->sec[i];
+	}
+
+
+	return NULL;
+}
+
+
+/**
+ * @brief dump the name of all elf sections
+ */
+
+void dump_sections(const struct elf_module *m)
+{
+	size_t i;
+
+
+	printk("\nSECTIONS:\n"
+	       "============================\n"
+	       "\t[NUM]\t[NAME]\t\t\t[TYPE]\t\t[SIZE]\t[ENTSZ]\t[FLAGS]\n");
+
+
+	for (i = 0; i < m->ehdr->e_shnum; i++) {
+
+		printk("\t%d\t%-20s", i, m->sh_str + m->shdr[i].sh_name);
+
+		switch (m->shdr[i].sh_type) {
+		case SHT_NULL:
+			printk("%-10s", "\tNULL"); break;
+		case SHT_PROGBITS:
+			printk("%-10s", "\tPROGBITS");
+			break;
+		case SHT_SYMTAB:
+			printk("%-10s", "\tSYMTAB"); break;
+		case SHT_STRTAB:
+			printk("%-10s", "\tSTRTAB"); break;
+		case SHT_RELA  :
+			printk("%-10s", "\tRELA"); break;
+		case SHT_HASH  :
+			printk("%-10s", "\tHASH"); break;
+		case SHT_DYNAMIC:
+			printk("%-10s", "\tDYNAMIC"); break;
+		case SHT_NOTE  :
+			printk("%-10s", "\tNOTE"); break;
+		case SHT_NOBITS:
+			printk("%-10s", "\tNOBITS"); break;
+		case SHT_REL   :
+			printk("%-10s", "\tREL"); break;
+		case SHT_SHLIB :
+			printk("%-10s", "\tSHLIB"); break;
+		case SHT_DYNSYM:
+			printk("%-10s", "\tDYNSYM"); break;
+		case SHT_NUM   :
+			printk("%-10s", "\tNUM"); break;
+		default:
+			printk("%-10s", "\tOTHER"); break;
+		}
+
+		printk("\t%ld\t%ld\t", m->shdr[i].sh_size, m->shdr[i].sh_entsize);
+
+		if (m->shdr[i].sh_flags & SHF_WRITE)
+			printk("WRITE ");
+		if (m->shdr[i].sh_flags & SHF_ALLOC)
+			printk("ALLOC ");
+		if (m->shdr[i].sh_flags & SHF_EXECINSTR)
+			printk("EXECINSTR ");
+		if (m->shdr[i].sh_flags & SHF_TLS)
+			printk("TLS ");
+		if (m->shdr[i].sh_flags & SHF_MASKPROC)
+			printk("MASKPROC ");
+
+		printk("\n");
+
+	}
+
+}
+
+
+/**
+ * @brief get the type of a symbol 
+ *
+ * @return 1 if symbol has been found, 0 otherwise
+ */
+
+static unsigned long get_symbol_type(const struct elf_module *m,
+				     const char *name) 
+
+{
+	unsigned int i;
+	unsigned int idx;
+	size_t sym_cnt;
+	
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+
+	idx = find_sec(m, ".symtab");
+
+	if (!idx) {
+		printf("WARN: no .symtab section found\n");
+		return -1;
+	}
+
+	symtab = &m->shdr[idx];
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		printk("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		return -1;
+	}
+	
+	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
+	
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+	
+	for (i = 0; i < sym_cnt; i++) {
+		if(!strcmp(get_symbol_str(m, i), name)) {
+			return ELF_ST_TYPE(symbols[i].st_info);
+		}
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * @brief get the value of a symbol 
+ *
+ * @return 1 if symbol has been found, 0 otherwise
+ */
+
+static unsigned long get_symbol_value(const struct elf_module *m,
+				      const char *name, unsigned long *value) 
+
+{
+	unsigned int i;
+	unsigned int idx;
+	size_t sym_cnt;
+	
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+
+	idx = find_sec(m, ".symtab");
+
+	if (!idx) {
+		printf("WARN: no .symtab section found\n");
+		return -1;
+	}
+
+	symtab = &m->shdr[idx];
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		printf("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		return -1;
+	}
+	
+	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
+	
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+	
+	for (i = 0; i < sym_cnt; i++) {
+		if(!strcmp(get_symbol_str(m, i), name)) {
+			(*value) =  symbols[i].st_value;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * @brief dump the contents of .symtab
+ */
+
+static int dump_symtab(struct elf_module *m)
+{
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+	size_t sym_cnt;
+	unsigned int idx;
+	unsigned int i;
+
+
+
+	idx = find_sec(m, ".symtab");
+
+	if (!idx) {
+		printf("WARN: no .symtab section found\n");
+		return -1;
+	}
+
+	symtab = &m->shdr[idx];
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		printf("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		return -1;
+	}
+	
+	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
+
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+
+
+	printf("\n.symtab contains %d entries\n"
+	       "============================\n"
+	       "\t[NUM]\t[VALUE]\t\t\t[SIZE]\t[TYPE]\t[NAME]\n", sym_cnt);
+
+	
+	for (i = 0; i < sym_cnt; i++) {
+
+		printf("\t%d\t%016lx\t%4ld",
+		       i,
+		       symbols[i].st_value,
+		       symbols[i].st_size);
+
+		switch (ELF_ST_TYPE(symbols[i].st_info)) {
+		case STT_NOTYPE  :
+			printf("\tNOTYPE "); break;  
+		case STT_OBJECT  :
+			printf("\tOBJECT "); break;
+		case STT_FUNC    :
+			printf("\tFUNC   "); break;
+		case STT_SECTION :
+			printf("\tSECTION"); break;
+		case STT_FILE    :
+			printf("\tFILE   "); break;
+		case STT_COMMON  :
+			printf("\tCOMMON "); break;
+		case STT_TLS     :
+			printf("\tTLS    "); break;
+		default:
+			printf("\tUNKNOWN"); break;
+		}
+
+		printf("\t%-10s\n", get_symbol_str(m, i));
+
+	}
+	
+	return 0;	
+}
+
+
+
+
+/**
+ * @brief dump the contents of strtab
+ */
+
+__attribute__((unused))
+static void dump_strtab(const struct elf_module *m)
+{
+	size_t i;
+
+
+	if (!m->str)
+		return;
+
+
+	printf("\n.strtab:\n"
+	       "============================\n"
+	       "\t[OFF]\t[STR]\n");
+
+	while(i < m->sh_size) {
+		printf("\t[%d]\t%s\n", i, m->str + i);
+		i += strlen(m->str + i) + 1;
+	}
+
+	printf("\n\n");
+}
+
+
+/**
+ * @brief locate and set the ".shstrtab" section
+ *
+ * @return 0 if section header string table was not found, 1 otherwise
+ *
+ */
+
+static int set_shstrtab(struct elf_module *m)
+{
+	size_t i;
+
+	for (i = 0; i < m->ehdr->e_shnum; i++) {
+
+		if (m->shdr[i].sh_type != SHT_STRTAB)
+			continue;
+
+		/* section name index is not within table boundary, skip */
+		if (m->shdr[i].sh_size < m->shdr[i].sh_name)
+			continue;
+
+		m->sh_str  = (((char *) m->ehdr) + m->shdr[i].sh_offset);
+		m->sh_size = m->shdr[i].sh_size;
+
+		/* it's a string section and the size is ok,
+		 * now make sure it is the right one */
+		if (!strcmp(m->sh_str + m->shdr[i].sh_name, ".shstrtab"))
+			return 1;
+	}
+
+	m->sh_str = NULL;
+	m->sh_size = 0;
+
+	return 0;
+}
+
+/**
+ * @brief locate and set the ".dynstr" section
+ *
+ * @return 0 if section header string table was not found, 1 otherwise
+ *
+ */
+
+static int set_dynstr(struct elf_module *m)
+{
+	unsigned int idx;
+
+
+	idx = find_sec(m, ".dynstr");
+
+	if (idx) {
+		m->dyn_str  = (((char *) m->ehdr) + m->shdr[idx].sh_offset);
+		m->dyn_size = m->shdr[idx].sh_size;
+		return 1;
+	}
+	
+	m->dyn_str = NULL;
+	m->dyn_size = 0;
+
+	return 0;
+}
+
+
+/**
+ * @brief locate and set the ".strtab" section
+ *
+ * @return 0 if string table was not found, 1 otherwise
+ *
+ */
+
+static int set_strtab(struct elf_module *m)
+{
+	unsigned int idx;
+
+
+	idx = find_sec(m, ".strtab");
+
+	if (idx) {
+		m->str      = (((char *) m->ehdr) + m->shdr[idx].sh_offset);
+		m->str_size = m->shdr[idx].sh_size; 
+		return 1;
+	}
+
+	m->str = NULL;
+	m->str_size = 0;
+
+	return 0;
+}
+
+
+/**
+ * @brief setup the module structure
+ *
+ * @return -1 on error
+ */
+
+static int setup_module(struct elf_module *m)
+{
+	/* initialise module configuration */
+	m->pa       = 0;
+	m->va       = 0;
+	// XXX
+	//m->size     = 0;
+	m->align    = sizeof(void *);
+	m->dyn      = NULL;
+	m->dyn_size = 0;
+
+	m->sec      = NULL;
+
+	/* set section headers */
+	if (m->ehdr->e_shoff) {
+		m->shdr = (Elf_Shdr *) (((char *) m->ehdr) + m->ehdr->e_shoff);
+	} else {
+		m->shdr = NULL;
+		printf("ERR: no section header found\n");
+		return -1;
+	}
+
+	/* locate and set section header string table */
+	if (!set_shstrtab(m))
+		return -1;
+	
+	/* locate and set dynamic string table */
+	if (!set_dynstr(m)) {
+		printf("WARN: no dynamic string table found\n");
+	}
+
+	/* locate and set string table */
+	if (!set_strtab(m))
+		return -1;
+
+	/* set up for relocatable object */
+	if (m->ehdr->e_type == ET_REL) {
+		printf("TODO\n");
+
+		m->align = 0x200000;	/* PC */
+#if 0
+		int i;
+		m->size = 0;
+		for (i = 0; i < m->ehdr->e_shnum; i++) {
+			if ((m->shdr[i].sh_flags & SHF_ALLOC)) {
+				printf("Alloc section: %s, size %ld\n",
+				       m->sh_str + m->shdr[i].sh_name,
+				       m->shdr[i].sh_size);
+				
+				m->size += m->shdr[i].sh_size;
+
+				if (m->shdr[idx].sh_addralign > m->align)
+		
+					m->align = m->shdr[idx].sh_addralign;
+
+			}
+		}
+#endif
+	}
+
+	return 0;
+}
+
+
+
+static int module_load_mem(struct elf_module *m)
+{
+	unsigned int idx = 0;
+	unsigned long va_load;
+	unsigned long pa_load;
+
+	char *src;
+
+	Elf_Shdr *sec;
+
+	struct module_section *s;
+
+	void *mem;
+
+
+	mem = malloc(m->size);
+
+	if (!mem)
+		return -ENOMEM;
+
+	/* exec info */
+	m->va = (unsigned long) mem;
+	m->pa = (unsigned long) mem;
+
+
+	printf("\n\nLoading module run-time sections\n");
+
+	va_load = m->va;
+	pa_load = m->pa;
+
+	m->num_sec = get_num_alloc_sections(m);
+	m->sec = (struct module_section *)
+		 malloc(sizeof(struct module_section) * m->num_sec);
+
+	if (!m->sec)
+		return -1;
+
+
+	s = m->sec;
+
+	while (1) {
+
+		idx = find_shdr_alloc(m, idx + 1);
+		if (!idx)
+			break;
+
+		sec = get_sec(m, idx);
+
+
+		if (!sec->sh_size)	/* don't need those */
+			continue;
+
+		s->size = sec->sh_size;
+
+		src = get_shstrtab_str(m, idx);
+		s->name = malloc(strlen(src));
+
+		if (!s->name)
+			return -1;
+
+		strcpy(s->name, src);
+
+
+		if (sec->sh_type & SHT_NOBITS) {
+			printf("\tZero segment %10s at %p size %ld\n",
+			       s->name, (char *) va_load,
+			       sec->sh_size);
+
+			bzero((void *) va_load, s->size);
+		} else {
+			printf("\tCopy segment %10s from %p to %p size %ld\n",
+			       s->name,
+			       (char *) m->ehdr + sec->sh_offset,
+			       (char *) va_load,
+			       sec->sh_size);
+
+			memcpy((void *) va_load,
+			       (char *) m->ehdr + sec->sh_offset,
+			       sec->sh_size);
+		}
+
+		s->addr = va_load;
+		va_load = s->addr + s->size;
+		
+		s++;
+
+		if (s > &m->sec[m->num_sec]) {
+			printf("Error out of section memory\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int module_relocate(struct elf_module *m)
+{
+	unsigned int idx = 0;
+
+	size_t i;
+	size_t rel_cnt;
+
+	Elf_Shdr *sec;
+	
+
+
+	/* no dynamic linkage, so it's either self-contained or bugged, we'll
+	 * assume the former, so cross your fingers and hope for the best
+	 */
+	if (m->ehdr->e_type != ET_REL)
+		if (m->ehdr->e_type != ET_DYN)
+			return 0;
+
+
+	/* we only need RELA type relocations */
+
+	while (1) {
+
+		idx = find_sec_type(m, SHT_RELA, idx + 1);
+
+		if (!idx)
+			break;
+
+		sec = get_sec(m, idx);
+
+		printk("\nSection Header info: %ld\n", sec->sh_info);
+		
+		if (sec) {
+
+			Elf_Rela *relatab;
+
+			rel_cnt = sec->sh_size / sec->sh_entsize;
+
+			printk("Found %d RELA entries\n", rel_cnt);
+			/* relocation table in memory */
+			relatab = (Elf_Rela *) ((long)m->ehdr + sec->sh_offset);
+
+			for (i = 0; i < rel_cnt; i++) {
+
+				int reladdr;
+				unsigned int symsec = ELF_R_SYM(relatab[i].r_info);
+				char *symstr = get_symbol_str(m, symsec);
+				struct module_section *s;
+				struct module_section *text  = find_mod_sec(m, ".text");
+				
+				printf("OFF: %08lx INF: %8lx ADD: %3ld LNK: %ld SEC: %d NAME: %s\n\n",
+				       relatab[i].r_offset,
+				       relatab[i].r_info,
+				       relatab[i].r_addend,
+				       sec->sh_link,
+				       symsec,
+				       symstr);
+			
+
+
+				if (strlen(symstr)) {
+					Elf_Addr sym = (Elf_Addr) lookup_symbol(symstr);
+
+					if (!sym) {
+
+						unsigned long symval;
+						printf("\tNot found in library, resolving in module\n");
+
+
+						if (!(get_symbol_type(m, symstr) & STT_FUNC)) {
+							printf("\tERROR, unresolved symbol %s\n", symstr);
+							return -1;
+						}
+						if (!get_symbol_value(m, symstr, &symval)) {
+							printf("\tERROR, unresolved symbol %s\n", symstr);
+							return -1;
+						}
+
+						sym = (text->addr + symval);
+
+					}
+
+					printf("\tSymbol %s at %lx\n", symstr, sym);
+
+					apply_relocate_add(m, &relatab[i], sym);
+
+				} else  { /* no string, symtab entry is probably a section, try to identify it */
+				
+					char *secstr = get_shstrtab_str(m, symsec);
+
+					s = find_mod_sec(m, secstr);
+
+					if (!s) {
+						printf("Error cannot locate section %s for symbol\n", secstr);
+							continue;
+					}
+					/* target address to insert at location */
+					reladdr = (long) s->addr;
+					printf("\tRelative symbol address: %x, entry at %08lx\n", reladdr, s->addr);
+					
+					apply_relocate_add(m, &relatab[i], reladdr);
+				}
+
+				printf("\n");
+
+
+			}
+			printf("\n");
+		}
+	}
+
+
+	return 0;
+}
+
+
+
+
+
+typedef void (*entrypoint_t)(void);
+void go(entrypoint_t ep)
+{
+	ep();
+	printk("done\n");
+}
+
+
+int module_load(struct elf_module *m, void *p)
+{
+	unsigned long symval;
+	entrypoint_t ep; 
+
+
+	/* the ELF binary starts with the ELF header */
+	m->ehdr = (Elf_Ehdr *) p;
+
+	printk("Checking ELF header\n");
+
+	if (elf_header_check(m->ehdr))
+		return -1;
+
+	printk("Setting up module configuration\n");
+	
+	if (setup_module(m))
+		return -1;
+
+
+	dump_sections(m);
+	
+	if (module_load_mem(m))
+		return -1;
+	
+	dump_symtab(m);
+
+	if (module_relocate(m))
+		return -1;
+
+#if 1
+	//uintptr_t epaddr = (uintptr_t) (m->va);
+	if (!get_symbol_value(m, "_module_init", &symval)) {
+		printf("module init not found\n");
+		return -1;
+
+	}
+
+	ep = (entrypoint_t) (m->va + symval);
+	
+	printf("Binary entrypoint is %lx; invoking %p\n", m->ehdr->e_entry, ep);
+
+	go(ep);
+#endif
+
+	return 0;
+}
+
+
+
+
diff --git a/kernel/testmodule.c b/kernel/testmodule.c
new file mode 100644
index 0000000000000000000000000000000000000000..362018baf2465d04eb87e9cd7336c7b00310f1ff
--- /dev/null
+++ b/kernel/testmodule.c
@@ -0,0 +1,23 @@
+/**
+ * @file kernel/test.c
+ */
+
+#include <page.h>
+
+#include <kernel/printk.h>
+
+
+void _module_init(void);
+
+void _module_init(void)
+{
+	int i;
+
+	printk("moopmoop\n");
+	
+	for (i = 0; i < 10; i++)
+		printk("the cow says: %d\n", i);
+
+	printk("allocating: %p\n", page_alloc());
+
+}
diff --git a/scripts/gen_ksym.awk b/scripts/gen_ksym.awk
new file mode 100755
index 0000000000000000000000000000000000000000..0f56712f2624d0525f0a2545bf35d7cbaf505424
--- /dev/null
+++ b/scripts/gen_ksym.awk
@@ -0,0 +1,4 @@
+#!/usr/bin/awk  -f
+BEGIN{ print "#include <stddef.h>\n #include \"include/kernel/ksym.h\""; print "struct ksym __ksyms[]={" }
+{ if(NF==3){print "{\"" $3 "\", (void*) 0x" $1 "},"}}
+END{print "{NULL,NULL} };"}
diff --git a/scripts/link-leanos.sh b/scripts/link-leanos.sh
new file mode 100755
index 0000000000000000000000000000000000000000..af6aa7aa455416fefb4393725fe406b12f8905f5
--- /dev/null
+++ b/scripts/link-leanos.sh
@@ -0,0 +1,293 @@
+#!/bin/sh
+#
+# link leanos
+#
+# leanos is linked from the objects selected by $(KBUILD_LEANOS_INIT) and
+# $(KBUILD_LEANOS_MAIN). Most are built-in.o files from top-level directories
+# in the kernel tree, others are specified in arch/$(ARCH)/Makefile.
+# Ordering when linking is important, and $(KBUILD_LEANOS_INIT) must be first.
+#
+# leanos
+#   ^
+#   |
+#   +-< $(KBUILD_LEANOS_INIT)
+#   |   +--< init/version.o + more
+#   |
+#   +--< $(KBUILD_LEANOS_MAIN)
+#   |    +--< drivers/built-in.o mm/built-in.o + more
+#   |
+#   +-< ${kallsymso} (see description in KALLSYMS section)
+#
+# leanos version (uname -v) cannot be updated during normal
+# descending-into-subdirs phase since we do not yet know if we need to
+# update leanos.
+# Therefore this step is delayed until just before final link of leanos.
+#
+# System.map is generated to document addresses of all kernel symbols
+
+# Error out on error
+set -e
+
+# Nice output in kbuild format
+# Will be supressed by "make -s"
+info()
+{
+	if [ "${quiet}" != "silent_" ]; then
+		printf "  %-7s %s\n" ${1} ${2}
+	fi
+}
+
+# Thin archive build here makes a final archive with
+# symbol table and indexes from leanos objects, which can be
+# used as input to linker.
+#
+# Traditional incremental style of link does not require this step
+#
+# built-in.o output file
+#
+archive_builtin()
+{
+	if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+		info AR built-in.o
+		rm -f built-in.o;
+		${AR} rcsT${KBUILD_ARFLAGS} built-in.o			\
+					${KBUILD_LEANOS_INIT}		\
+					${KBUILD_LEANOS_MAIN}
+	fi
+}
+
+# Link of leanos.o used for section mismatch analysis
+# ${1} output file
+modpost_link()
+{
+	local objects
+
+	if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+		objects="--whole-archive built-in.o"
+	else
+		objects="${KBUILD_LEANOS_INIT}				\
+			--start-group					\
+			${KBUILD_LEANOS_MAIN}				\
+			--end-group"
+	fi
+	${LD} ${LDFLAGS} -r -o ${1} ${objects}
+}
+
+# Link of leanos
+# ${1} - optional extra .o files
+# ${2} - output file
+leanos_link()
+{
+	local lds="${objtree}/${KBUILD_LDS}"
+	local objects
+
+
+	# since we link against the BCC libc at this time, we'll just
+	# call $CC instead of LD
+	${CC} ${LDFLAGS} ${LDFLAGS_leanos} -o ${2}		\
+		${KBUILD_LEANOS_INIT} ${KBUILD_LEANOS_MAIN} ${1}
+
+#	if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+#		objects="--whole-archive built-in.o ${1}"
+#	else
+#		objects="${KBUILD_LEANOS_INIT}			\
+#			--start-group				\
+#			${KBUILD_LEANOS_MAIN}			\
+#			--end-group				\
+#			${1}"
+#	fi
+#
+#	${LD} ${LDFLAGS} ${LDFLAGS_leanos} -o ${2}		\
+#		-T ${lds} ${objects}
+
+}
+
+
+# Create ${2} .o file with all symbols from the ${1} object file
+kallsyms()
+{
+	info KSYM ${2}
+	local kallsymopt;
+
+
+	if [ -n "${CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX}" ]; then
+		kallsymopt="${kallsymopt} --symbol-prefix=_"
+	fi
+
+	if [ -n "${CONFIG_KALLSYMS_ALL}" ]; then
+		kallsymopt="${kallsymopt} --all-symbols"
+	fi
+
+	if [ -n "${CONFIG_KALLSYMS_ABSOLUTE_PERCPU}" ]; then
+		kallsymopt="${kallsymopt} --absolute-percpu"
+	fi
+
+	if [ -n "${CONFIG_KALLSYMS_BASE_RELATIVE}" ]; then
+		kallsymopt="${kallsymopt} --base-relative"
+	fi
+
+	local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL}               \
+		      ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
+
+	local afile="`basename ${2} .o`.c"
+
+	${NM} -n ${1} | scripts/gen_ksym.awk > ${afile}
+	#${NM} -n ${1} | scripts/kallsyms ${kallsymopt} > ${afile}
+	${CC} ${aflags} -c -o ${2} ${afile}
+}
+
+# Create map file with all symbols from ${1}
+# See mksymap for additional details
+mksysmap()
+{
+	${CONFIG_SHELL} "${srctree}/scripts/mksysmap" ${1} ${2}
+}
+
+sortextable()
+{
+	${objtree}/scripts/sortextable ${1}
+}
+
+# Delete output files in case of error
+cleanup()
+{
+	rm -f .old_version
+	rm -f .tmp_System.map
+	rm -f .tmp_kallsyms*
+	rm -f .tmp_version
+	rm -f .tmp_leanos*
+	rm -f built-in.o
+	rm -f System.map
+	rm -f leanos
+	rm -f leanos.o
+}
+
+on_exit()
+{
+	if [ $? -ne 0 ]; then
+		cleanup
+	fi
+}
+trap on_exit EXIT
+
+on_signals()
+{
+	exit 1
+}
+trap on_signals HUP INT QUIT TERM
+
+#
+#
+# Use "make V=1" to debug this script
+case "${KBUILD_VERBOSE}" in
+*1*)
+	set -x
+	;;
+esac
+
+if [ "$1" = "clean" ]; then
+	cleanup
+	exit 0
+fi
+
+# We need access to CONFIG_ symbols
+case "${KCONFIG_CONFIG}" in
+*/*)
+	. "${KCONFIG_CONFIG}"
+	;;
+*)
+	# Force using a file from the current directory
+	. "./${KCONFIG_CONFIG}"
+esac
+
+archive_builtin
+
+#link leanos.o
+info LD leanos.o
+modpost_link leanos.o
+
+# modpost leanos.o to check for section mismatches
+${MAKE} -f "${srctree}/scripts/Makefile.modpost" leanos.o
+
+# Update version
+info GEN .version
+if [ ! -r .version ]; then
+	rm -f .version;
+	echo 1 >.version;
+else
+	mv .version .old_version;
+	expr 0$(cat .old_version) + 1 >.version;
+fi;
+
+# final build of init/
+${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init GCC_PLUGINS_CFLAGS="${GCC_PLUGINS_CFLAGS}"
+
+kallsymso=""
+kallsyms_leanos=""
+if [ -n "${CONFIG_KALLSYMS}" ]; then
+
+	# kallsyms support
+	# Generate section listing all symbols and add it into leanos
+	# It's a three step process:
+	# 1)  Link .tmp_leanos1 so it has all symbols and sections,
+	#     but __kallsyms is empty.
+	#     Running kallsyms on that gives us .tmp_kallsyms1.o with
+	#     the right size
+	# 2)  Link .tmp_leanos2 so it now has a __kallsyms section of
+	#     the right size, but due to the added section, some
+	#     addresses have shifted.
+	#     From here, we generate a correct .tmp_kallsyms2.o
+	# 2a) We may use an extra pass as this has been necessary to
+	#     woraround some alignment related bugs.
+	#     KALLSYMS_EXTRA_PASS=1 is used to trigger this.
+	# 3)  The correct ${kallsymso} is linked into the final leanos.
+	#
+	# a)  Verify that the System.map from leanos matches the map from
+	#     ${kallsymso}.
+
+	kallsymso=.tmp_kallsyms2.o
+	kallsyms_leanos=.tmp_leanos2
+
+	# step 1
+	leanos_link "" .tmp_leanos1
+	kallsyms .tmp_leanos1 .tmp_kallsyms1.o
+
+	# step 2
+	leanos_link .tmp_kallsyms1.o .tmp_leanos2
+	kallsyms .tmp_leanos2 .tmp_kallsyms2.o
+
+	# step 2a
+	if [ -n "${KALLSYMS_EXTRA_PASS}" ]; then
+		kallsymso=.tmp_kallsyms3.o
+		kallsyms_leanos=.tmp_leanos3
+
+		leanos_link .tmp_kallsyms2.o .tmp_leanos3
+
+		kallsyms .tmp_leanos3 .tmp_kallsyms3.o
+	fi
+fi
+
+info LD leanos
+leanos_link "${kallsymso}" leanos
+
+if [ -n "${CONFIG_BUILDTIME_EXTABLE_SORT}" ]; then
+	info SORTEX leanos
+	sortextable leanos
+fi
+
+info SYSMAP System.map
+mksysmap leanos System.map
+
+# step a (see comment above)
+if [ -n "${CONFIG_KALLSYMS}" ]; then
+	mksysmap ${kallsyms_leanos} .tmp_System.map
+
+	if ! cmp -s System.map .tmp_System.map; then
+		echo >&2 Inconsistent kallsyms data
+		echo >&2 Try "make KALLSYMS_EXTRA_PASS=1" as a workaround
+		exit 1
+	fi
+fi
+
+# We made a new kernel - delete old version file
+rm -f .old_version
diff --git a/scripts/mksysmap b/scripts/mksysmap
new file mode 100755
index 0000000000000000000000000000000000000000..a35acc0d0b827fa056348a76d074d5ef0a3dae93
--- /dev/null
+++ b/scripts/mksysmap
@@ -0,0 +1,44 @@
+#!/bin/sh -x
+# Based on the vmlinux file create the System.map file
+# System.map is used by module-init tools and some debugging
+# tools to retrieve the actual addresses of symbols in the kernel.
+#
+# Usage
+# mksysmap vmlinux System.map
+
+
+#####
+# Generate System.map (actual filename passed as second argument)
+
+# $NM produces the following output:
+# f0081e80 T alloc_vfsmnt
+
+#   The second row specify the type of the symbol:
+#   A = Absolute
+#   B = Uninitialised data (.bss)
+#   C = Common symbol
+#   D = Initialised data
+#   G = Initialised data for small objects
+#   I = Indirect reference to another symbol
+#   N = Debugging symbol
+#   R = Read only
+#   S = Uninitialised data for small objects
+#   T = Text code symbol
+#   U = Undefined symbol
+#   V = Weak symbol
+#   W = Weak symbol
+#   Corresponding small letters are local symbols
+
+# For System.map filter away:
+#   a - local absolute symbols
+#   U - undefined global symbols
+#   N - debugging symbols
+#   w - local weak symbols
+
+# readprofile starts reading symbols when _stext is found, and
+# continue until it finds a symbol which is not either of 'T', 't',
+# 'W' or 'w'. __crc_ are 'A' and placed in the middle
+# so we just ignore them to let readprofile continue to work.
+# (At least sparc64 has __crc_ in the middle).
+
+$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\( .L\)' > $2