diff --git a/linux-user/main.c b/linux-user/main.c
index a0d8ce7..7db9fc3 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2725,12 +2725,9 @@ int main(int argc, char **argv, char **envp)
     /*
      * Read in mmap_min_addr kernel parameter.  This value is used
      * When loading the ELF image to determine whether guest_base
-     * is needed.
-     *
-     * When user has explicitly set the quest base, we skip this
-     * test.
+     * is needed.  It is also used in mmap_find_vma.
      */
-    if (!have_guest_base) {
+    {
         FILE *fp;
 
         if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 144fb7c..b92fdc4 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -281,8 +281,9 @@ unsigned long last_brk;
  */
 abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
 {
-    void *ptr;
+    void *ptr, *prev;
     abi_ulong addr;
+    int wrapped, repeat;
 
     size = HOST_PAGE_ALIGN(size);
     start &= qemu_host_page_mask;
@@ -292,8 +293,11 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
         start = mmap_next_start;
 
     addr = start;
+    wrapped = (start == 0);
+    repeat = 0;
+    prev = 0;
 
-    for(;;) {
+    for (;; prev = ptr) {
         /*
          * Reserve needed memory area to avoid a race.
          * It should be discarded using:
@@ -305,20 +309,69 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
                    MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
 
         /* ENOMEM, if host address space has no memory */
-        if (ptr == MAP_FAILED)
+        if (ptr == MAP_FAILED) {
             return (abi_ulong)-1;
+        }
 
-        /* If address fits target address space we've found what we need */
-        if ((unsigned long)ptr + size - 1 <= (abi_ulong)-1)
+        /* Count the number of sequential returns of the same address.
+           This is used to modify the search algorithm below.  */
+        repeat = (ptr == prev ? repeat + 1 : 0);
+
+        if ((unsigned long)ptr & ~TARGET_PAGE_MASK) {
+            /* The address is not properly aligned for the target.  */
+            switch (repeat) {
+            case 0:
+                /* Assume the result that the kernel gave us is the
+                   first with enough free space, so start again at the
+                   next higher target page.  */
+                addr = TARGET_PAGE_ALIGN((unsigned long)ptr);
+                break;
+            case 1:
+                /* Sometimes the kernel decides to perform the allocation
+                   at the top end of memory instead.  Notice this via
+                   sequential allocations that result in the same address.  */
+                /* ??? This can be exacerbated by a successful allocation
+                   at the top of memory on one round, and storing that
+                   result in mmap_next_start.  The next allocation is sure
+                   to start at an address that's going to fail.  */
+                addr = (unsigned long)ptr & TARGET_PAGE_MASK;
+                break;
+            case 2:
+                /* Start over at low memory.  */
+                addr = 0;
+                break;
+            default:
+                /* Fail.  This unaligned block must be the only one left.  */
+                addr = -1;
+                break;
+            }
+        } else if ((unsigned long)ptr + size - 1 <= (abi_ulong)-1) {
             break;
+        } else {
+            /* Since the result the kernel gave didn't fit, start
+               again at low memory.  If any repetition, fail.  */
+            addr = (repeat ? -1 : 0);
+        }
 
-        /* Unmap and try again with new page */
+        /* Unmap and try again.  */
         munmap(ptr, size);
-        addr += qemu_host_page_size;
 
-        /* ENOMEM if we check whole of target address space */
-        if (addr == start)
+        /* ENOMEM if we checked the whole of the target address space.  */
+        if (addr == -1) {
             return (abi_ulong)-1;
+        } else if (addr == 0) {
+            if (wrapped) {
+                return (abi_ulong)-1;
+            }
+            wrapped = 1;
+            /* Don't actually use 0 when wrapping, instead indicate
+               that we'd truely like an allocation in low memory.  */
+            addr = (mmap_min_addr > TARGET_PAGE_SIZE
+                    ? TARGET_PAGE_ALIGN(mmap_min_addr)
+                    : TARGET_PAGE_SIZE);
+        } else if (wrapped && addr >= start) {
+            return (abi_ulong)-1;
+        }
     }
 
     /* Update default start address */
