kern/176216: [patch] Allow loading ELF libraries at their preferred base address (needed for Wine)

Damjan Jovanovic damjan.jov at gmail.com
Sun Feb 17 21:00:00 UTC 2013


>Number:         176216
>Category:       kern
>Synopsis:       [patch] Allow loading ELF libraries at their preferred base address (needed for Wine)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Feb 17 21:00:00 UTC 2013
>Closed-Date:
>Last-Modified:
>Originator:     Damjan Jovanovic
>Release:        9.1
>Organization:
>Environment:
Any
>Description:
FreeBSD's dynamic linker (/libexec/ld-elf.so.1) currently loads libraries by calling mmap() with 0 as the first parameter, causing them to be loaded at whatever address the kernel chooses.

But for maximum compatibility with Windows applications, Wine needs some of its DLLs to be loaded at particular memory addresses (see the comments on https://wiki.freebsd.org/Wine).

With this patch, first mmap() is called with the base address of the library and MAP_FIXED, giving the library a chance to load at the base address it specified, and if this fails then mmap() is called without MAP_FIXED so the library can at least load somewhere else since its preferred memory region is unavailable.

>How-To-Repeat:

>Fix:


Patch attached with submission follows:

Index: libexec/rtld-elf/map_object.c
===================================================================
--- libexec/rtld-elf/map_object.c	(revision 246877)
+++ libexec/rtld-elf/map_object.c	(working copy)
@@ -175,11 +175,22 @@
     base_vaddr = trunc_page(segs[0]->p_vaddr);
     base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz);
     mapsize = base_vlimit - base_vaddr;
-    base_addr = hdr->e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL;
+    base_addr = (caddr_t) base_vaddr;
 
-    mapbase = mmap(base_addr, mapsize, PROT_NONE, MAP_ANON | MAP_PRIVATE |
-      MAP_NOCORE, -1, 0);
+    /*
+     * Executables, and libraries whose base_addr isn't 0,
+     * should ideally be loaded at that base_addr.
+     */
+    mapbase = (caddr_t) -1;
+    if (base_addr != 0) {
+        mapbase = mmap(base_addr, mapsize, PROT_NONE, MAP_ANON | MAP_PRIVATE |
+          MAP_NOCORE | MAP_FIXED, -1, 0);
+    }
     if (mapbase == (caddr_t) -1) {
+        mapbase = mmap(base_addr, mapsize, PROT_NONE, MAP_ANON | MAP_PRIVATE |
+          MAP_NOCORE, -1, 0);
+    }
+    if (mapbase == (caddr_t) -1) {
 	_rtld_error("%s: mmap of entire address space failed: %s",
 	  path, rtld_strerror(errno));
 	goto error;


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list