Index: uspace/lib/c/generic/elf/elf_load.c
===================================================================
--- uspace/lib/c/generic/elf/elf_load.c	(revision 66855b2198cfc74d61f61da3149b75b7e8a013b1)
+++ uspace/lib/c/generic/elf/elf_load.c	(revision 7465910ad89e682d478e914c4ab99065a339a982)
@@ -62,5 +62,5 @@
 	int rc;
 
-	rc = elf_load_file(file, 0, 0, &info->finfo);
+	rc = elf_load_file(file, 0, &info->finfo);
 	if (rc != EE_OK) {
 		DPRINTF("Failed to load executable '%s'.\n", file_name);
Index: uspace/lib/c/generic/elf/elf_mod.c
===================================================================
--- uspace/lib/c/generic/elf/elf_mod.c	(revision 66855b2198cfc74d61f61da3149b75b7e8a013b1)
+++ uspace/lib/c/generic/elf/elf_mod.c	(revision 7465910ad89e682d478e914c4ab99065a339a982)
@@ -58,4 +58,5 @@
 #include <str_error.h>
 #include <stdlib.h>
+#include <macros.h>
 
 #include <elf/elf_load.h>
@@ -73,5 +74,5 @@
 };
 
-static unsigned int elf_load_module(elf_ld_t *elf, size_t so_bias);
+static unsigned int elf_load_module(elf_ld_t *elf);
 static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry);
 static int load_segment(elf_ld_t *elf, elf_segment_header_t *entry);
@@ -86,5 +87,4 @@
  *
  * @param file      ELF file.
- * @param so_bias   Bias to use if the file is a shared object.
  * @param info      Pointer to a structure for storing information
  *                  extracted from the binary.
@@ -93,5 +93,5 @@
  *
  */
-int elf_load_file(int file, size_t so_bias, eld_flags_t flags, elf_finfo_t *info)
+int elf_load_file(int file, eld_flags_t flags, elf_finfo_t *info)
 {
 	elf_ld_t elf;
@@ -110,5 +110,5 @@
 	elf.flags = flags;
 
-	int ret = elf_load_module(&elf, so_bias);
+	int ret = elf_load_module(&elf);
 
 	vfs_put(ofile);
@@ -116,11 +116,10 @@
 }
 
-int elf_load_file_name(const char *path, size_t so_bias, eld_flags_t flags,
-    elf_finfo_t *info)
+int elf_load_file_name(const char *path, eld_flags_t flags, elf_finfo_t *info)
 {
 	int file;
 	errno_t rc = vfs_lookup(path, 0, &file);
 	if (rc == EOK) {
-		int ret = elf_load_file(file, so_bias, flags, info);
+		int ret = elf_load_file(file, flags, info);
 		vfs_put(file);
 		return ret;
@@ -137,8 +136,7 @@
  *
  * @param elf		Pointer to loader state buffer.
- * @param so_bias	Bias to use if the file is a shared object.
  * @return EE_OK on success or EE_xx error code.
  */
-static unsigned int elf_load_module(elf_ld_t *elf, size_t so_bias)
+static unsigned int elf_load_module(elf_ld_t *elf)
 {
 	elf_header_t header_buf;
@@ -154,6 +152,4 @@
 		return EE_IO;
 	}
-
-	elf->header = header;
 
 	/* Identify ELF */
@@ -188,9 +184,92 @@
 	}
 
+	if (header->e_phoff == 0) {
+		DPRINTF("Program header table is not present!\n");
+		return EE_UNSUPPORTED;
+	}
+
+	/* Read program header table.
+	 * Normally, there are very few program headers, so don't bother
+	 * with allocating memory dynamically.
+	 */
+	const int phdr_cap = 16;
+	elf_segment_header_t phdr[phdr_cap];
+	size_t phdr_len = header->e_phnum * header->e_phentsize;
+
+	if (phdr_len > sizeof(phdr)) {
+		DPRINTF("more than %d program headers\n", phdr_cap);
+		return EE_UNSUPPORTED;
+	}
+
+	pos = header->e_phoff;
+	rc = vfs_read(elf->fd, &pos, phdr, phdr_len, &nr);
+	if (rc != EOK || nr != phdr_len) {
+		DPRINTF("Read error.\n");
+		return EE_IO;
+	}
+
+	uintptr_t module_base = UINTPTR_MAX;
+	uintptr_t module_top = 0;
+	uintptr_t base_offset = UINTPTR_MAX;
+
+	/* Walk through PT_LOAD headers, to find out the size of the module. */
+	for (i = 0; i < header->e_phnum; i++) {
+		if (phdr[i].p_type != PT_LOAD)
+			continue;
+
+		if (module_base > phdr[i].p_vaddr) {
+			module_base = phdr[i].p_vaddr;
+			base_offset = phdr[i].p_offset;
+		}
+		module_top = max(module_top, phdr[i].p_vaddr + phdr[i].p_memsz);
+	}
+
+	if (base_offset != 0) {
+		DPRINTF("ELF headers not present in the text segment.\n");
+		return EE_INVALID;
+	}
+
 	/* Shared objects can be loaded with a bias */
-	if (header->e_type == ET_DYN)
-		elf->bias = so_bias;
-	else
+	if (header->e_type != ET_DYN) {
 		elf->bias = 0;
+	} else {
+		if (module_base != 0) {
+			DPRINTF("Unexpected shared object format.\n");
+			return EE_INVALID;
+		}
+
+		/* Attempt to allocate a span of memory large enough for the
+		 * shared object.
+		 */
+		// FIXME: This is not reliable when we're running
+		//        multi-threaded. Even if this part succeeds, later
+		//        allocation can fail because another thread took the
+		//        space in the meantime. This is only relevant for
+		//        dlopen() though.
+		void *area = as_area_create(AS_AREA_ANY, module_top,
+		    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE |
+		    AS_AREA_LATE_RESERVE, AS_AREA_UNPAGED);
+
+		if (area == AS_MAP_FAILED) {
+			DPRINTF("Can't find suitable memory area.\n");
+			return EE_MEMORY;
+		}
+
+		elf->bias = (uintptr_t) area;
+		as_area_destroy(area);
+	}
+
+	/* Load all loadable segments. */
+	for (i = 0; i < header->e_phnum; i++) {
+		if (phdr[i].p_type != PT_LOAD)
+			continue;
+
+		ret = load_segment(elf, &phdr[i]);
+		if (ret != EE_OK)
+			return ret;
+	}
+
+	void *base = (void *) module_base + elf->bias;
+	elf->info->base = base;
 
 	/* Ensure valid TLS info even if there is no TLS header. */
@@ -205,15 +284,8 @@
 	/* Walk through all segment headers and process them. */
 	for (i = 0; i < header->e_phnum; i++) {
-		elf_segment_header_t segment_hdr;
-
-		pos = header->e_phoff + i * sizeof(elf_segment_header_t);
-		rc = vfs_read(elf->fd, &pos, &segment_hdr,
-		    sizeof(elf_segment_header_t), &nr);
-		if (rc != EOK || nr != sizeof(elf_segment_header_t)) {
-			DPRINTF("Read error.\n");
-			return EE_IO;
-		}
-
-		ret = segment_header(elf, &segment_hdr);
+		if (phdr[i].p_type == PT_LOAD)
+			continue;
+
+		ret = segment_header(elf, &phdr[i]);
 		if (ret != EE_OK)
 			return ret;
@@ -270,14 +342,8 @@
 	case PT_NOTE:
 		break;
-	case PT_LOAD:
-		return load_segment(elf, entry);
-		break;
 	case PT_INTERP:
 		elf->info->interp =
 		    (void *)((uint8_t *)entry->p_vaddr + elf->bias);
 
-		// FIXME: This actually won't work, because the text segment is
-		// not loaded yet.
-#if 0
 		if (elf->info->interp[entry->p_filesz - 1] != '\0') {
 			DPRINTF("Unterminated ELF interp string.\n");
@@ -285,5 +351,4 @@
 		}
 		DPRINTF("interpreter: \"%s\"\n", elf->info->interp);
-#endif
 		break;
 	case PT_DYNAMIC:
Index: uspace/lib/c/generic/rtld/module.c
===================================================================
--- uspace/lib/c/generic/rtld/module.c	(revision 66855b2198cfc74d61f61da3149b75b7e8a013b1)
+++ uspace/lib/c/generic/rtld/module.c	(revision 7465910ad89e682d478e914c4ab99065a339a982)
@@ -202,16 +202,16 @@
 	str_cpy(name_buf + 5, NAME_BUF_SIZE - 5, name);
 
-	/* FIXME: need to real allocation of address space */
-	m->bias = rtld->next_bias;
-	rtld->next_bias += 0x100000;
 
 	DPRINTF("filename:'%s'\n", name_buf);
-	DPRINTF("load '%s' at 0x%zx\n", name_buf, m->bias);
-
-	rc = elf_load_file_name(name_buf, m->bias, ELDF_RW, &info);
+
+	rc = elf_load_file_name(name_buf, ELDF_RW, &info);
 	if (rc != EE_OK) {
 		printf("Failed to load '%s'\n", name_buf);
 		exit(1);
 	}
+
+	m->bias = elf_get_bias(info.base);
+
+	DPRINTF("loaded '%s' at 0x%zx\n", name_buf, m->bias);
 
 	if (info.dynamic == NULL) {
Index: uspace/lib/c/generic/rtld/rtld.c
===================================================================
--- uspace/lib/c/generic/rtld/rtld.c	(revision 66855b2198cfc74d61f61da3149b75b7e8a013b1)
+++ uspace/lib/c/generic/rtld/rtld.c	(revision 7465910ad89e682d478e914c4ab99065a339a982)
@@ -53,5 +53,4 @@
 	list_initialize(&runtime_env->modules);
 	list_initialize(&runtime_env->imodules);
-	runtime_env->next_bias = 0x2000000;
 	runtime_env->program = NULL;
 	runtime_env->next_id = 1;
@@ -121,7 +120,4 @@
 	env->program = prog;
 
-	/* Work around non-existent memory space allocation. */
-	env->next_bias = 0x1000000;
-
 	/*
 	 * Now we can continue with loading all other modules.
Index: uspace/lib/c/include/elf/elf_mod.h
===================================================================
--- uspace/lib/c/include/elf/elf_mod.h	(revision 66855b2198cfc74d61f61da3149b75b7e8a013b1)
+++ uspace/lib/c/include/elf/elf_mod.h	(revision 7465910ad89e682d478e914c4ab99065a339a982)
@@ -78,4 +78,9 @@
 	entry_point_t entry;
 
+	/** The base address where the file has been loaded.
+	 *  Points to the ELF file header.
+	 */
+	void *base;
+
 	/** ELF interpreter name or NULL if statically-linked */
 	const char *interp;
@@ -101,7 +106,4 @@
 	eld_flags_t flags;
 
-	/** A copy of the ELF file header */
-	elf_header_t *header;
-
 	/** Store extracted info here */
 	elf_finfo_t *info;
@@ -109,6 +111,6 @@
 
 extern const char *elf_error(unsigned int);
-extern int elf_load_file(int, size_t, eld_flags_t, elf_finfo_t *);
-extern int elf_load_file_name(const char *, size_t, eld_flags_t, elf_finfo_t *);
+extern int elf_load_file(int, eld_flags_t, elf_finfo_t *);
+extern int elf_load_file_name(const char *, eld_flags_t, elf_finfo_t *);
 
 #endif
Index: uspace/lib/c/include/types/rtld/rtld.h
===================================================================
--- uspace/lib/c/include/types/rtld/rtld.h	(revision 66855b2198cfc74d61f61da3149b75b7e8a013b1)
+++ uspace/lib/c/include/types/rtld/rtld.h	(revision 7465910ad89e682d478e914c4ab99065a339a982)
@@ -61,7 +61,4 @@
 	/** List of initial modules */
 	list_t imodules;
-
-	/** Temporary hack to place each module at different address. */
-	uintptr_t next_bias;
 } rtld_t;
 
