Index: uspace/lib/c/generic/elf/elf_mod.c
===================================================================
--- uspace/lib/c/generic/elf/elf_mod.c	(revision 4c5f04f66aa0d34ff3bbed6e9abebf85f59723fb)
+++ uspace/lib/c/generic/elf/elf_mod.c	(revision 6e33c2f45b5ed2571a37a24955c8a4fb7316dddb)
@@ -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:
