Implement binary relocatability

Mike Hearn mike at theoretic.com
Thu Jan 22 13:39:37 CST 2004


Hi,

Here's a first cut at allowing wine to be installed to any prefix at
runtime and still work nicely. I have implemented this for some customers
who are finding it a bit tricky to work with the source tarball I sent
them, so I want to make a binary tarball and let them extract it to
wherever so it can be installed side-by-side with a pre-existing wine.

It is unfortunately Linux specific, though that's more because I know how
to do this on Linux but not on any other platforms. It'd not be too hard
to adapt to other platforms if they provide the same information Linux
does to userland.

Here's how it works:

* A configure test is added that checks for the existence of
/proc/self/maps, so you will need to run "autoheader && autoconf" to make
this work.

* A new file is added to libs/wine, prefix.c, which looks up the
base address of libwine by using dladdr on an empty string (ie an entry in
the data segment). We then scan /proc/self/maps looking for that base
address, from which we can then get the absolute path of the library. This
file is a stripped down form of a generic kit, which is why it might look
a bit strange at first.

* The dynamically derived absolute path of the dlls directory is appended
to dll_paths

* We set an RPATH on the wine binaries: wineserver, wine-glibc,
wine-pthread and wine-kthread using the ELF ${ORIGIN} feature, so they can
locate libwine without needing LD_LIBRARY_PATH or /etc/ld.so.conf set.

These changes combined mean you can do the following:

./configure
make depend && make
make install prefix=/opt/wine1

/opt/wine1/bin/wine
(works)

mv /opt/wine1 /opt/wine2
/opt/wine2/bin/wine
(still works)

Before Wine would not be able to find libwine nor ntdll.so, now it can.

I hope this patch is OK, as I think this is a useful feature that far more
free software should support. The list of use cases is big.

ChangeLog:
Mike Hearn <mike at theoretic.com>
Hongli Lai <h.lai at chello.nl>
Implement binary relocatability for the wine loader and server

Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.232
diff -u -b -w -r1.232 configure.ac
--- configure.ac	20 Jan 2004 00:21:42 -0000	1.232
+++ configure.ac	22 Jan 2004 19:31:30 -0000
@@ -1408,6 +1408,12 @@
   *sun*) WINE_CHECK_DEFINE([__sun__]) ;;
 esac
 
+dnl *** check for /proc/self/maps to see if we can do binary relocatability on linux ***
+
+if test -e /proc/self/maps; then
+   AC_DEFINE(ENABLE_BINRELOC, 1, [Use binary relocatability])
+fi
+
 dnl **** Generate output files ****
 
 AH_TOP([#define __WINE_CONFIG_H])
Index: server/Makefile.in
===================================================================
RCS file: /home/wine/wine/server/Makefile.in,v
retrieving revision 1.48
diff -u -b -w -r1.48 Makefile.in
--- server/Makefile.in	10 Dec 2003 04:08:06 -0000	1.48
+++ server/Makefile.in	22 Jan 2004 19:31:30 -0000
@@ -48,12 +48,14 @@
 
 PROGRAMS = wineserver
 
+RPATH         = -Wl,-rpath -Wl,'$${ORIGIN}/../lib'
+
 all: $(PROGRAMS)
 
 @MAKE_RULES@
 
 wineserver: $(OBJS)
-	$(CC) -o $(PROGRAMS) $(OBJS) $(LIBWINE) $(LIBUNICODE) $(LIBPORT) $(LDFLAGS) $(LIBS)
+	$(CC) -o $(PROGRAMS) $(OBJS) $(LIBWINE) $(LIBUNICODE) $(LIBPORT) $(LDFLAGS) $(LIBS) $(RPATH)
 
 install:: $(PROGRAMS)
 	$(MKINSTALLDIRS) $(bindir)
Index: loader/Makefile.in
===================================================================
RCS file: /home/wine/wine/loader/Makefile.in,v
retrieving revision 1.14
diff -u -b -w -r1.14 Makefile.in
--- loader/Makefile.in	22 Nov 2003 00:08:26 -0000	1.14
+++ loader/Makefile.in	22 Jan 2004 19:31:30 -0000
@@ -16,6 +16,8 @@
 WINE_BINARIES = @WINE_BINARIES@
 MAIN_BINARY   = @MAIN_BINARY@
 
+RPATH         = -Wl,-rpath -Wl,'$${ORIGIN}/../lib'
+
 all: $(WINE_BINARIES) $(MODULE)
 
 @MAKE_RULES@
@@ -24,13 +26,13 @@
 LDEXECFLAGS = @LDEXECFLAGS@
 
 wine-glibc: glibc.o Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) glibc.o $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
+	$(CC) -o $@ $(LDEXECFLAGS) glibc.o $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS) $(RPATH)
 
 wine-kthread: $(KTHREAD_OBJS) Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS)
+	$(CC) -o $@ $(LDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS) $(RPATH)
 
 wine-pthread: $(PTHREAD_OBJS) Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) $(PTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
+	$(CC) -o $@ $(LDEXECFLAGS) $(PTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS) $(RPATH)
 
 $(MODULE): $(MAIN_BINARY)
 	$(RM) $(MODULE) && $(LN_S) $(MAIN_BINARY) $(MODULE)
Index: libs/wine/loader.c
===================================================================
RCS file: /home/wine/wine/libs/wine/loader.c,v
retrieving revision 1.16
diff -u -b -w -r1.16 loader.c
--- libs/wine/loader.c	2 Jan 2004 21:08:05 -0000	1.16
+++ libs/wine/loader.c	22 Jan 2004 19:31:30 -0000
@@ -41,6 +41,10 @@
 #include "winbase.h"
 #include "wine/library.h"
 
+#ifdef ENABLE_BINRELOC
+extern char *wine_locate (void *symbol);
+#endif
+
 /* argc/argv for the Windows application */
 int __wine_main_argc = 0;
 char **__wine_main_argv = NULL;
@@ -78,6 +82,27 @@
     static const char * const dlldir = DLLDIR;
     int len, count = 0;
     char *p, *path = getenv( "WINEDLLPATH" );
+    char *dynamic_dlldir = NULL;
+    
+#ifdef ENABLE_BINRELOC
+    char *slash;
+    char *relpath = "/wine";
+    
+    /* We want to determine the absolute path of this libwine from the linker mappings, then calculate a dlldir from that
+       For now, we'll assume a relative path of ./wine
+     */
+    dynamic_dlldir = wine_locate("");
+    slash = strrchr( dynamic_dlldir, '/' ); /* get to the prefix */
+    if (slash) {
+	*slash = '\0';
+	p = malloc( strlen(dynamic_dlldir) + strlen(relpath) + 1 );
+	strcpy( p, dynamic_dlldir );
+	strcat( p, relpath );
+	dynamic_dlldir = p;
+    } else dynamic_dlldir = NULL;
+    
+#endif
+
 
     if (path)
     {
@@ -93,7 +118,7 @@
         }
     }
 
-    dll_paths = malloc( (count+1) * sizeof(*dll_paths) );
+    dll_paths = malloc( (count + (dynamic_dlldir ? 1 : 2)) * sizeof(*dll_paths) );
 
     if (count)
     {
@@ -116,6 +141,11 @@
     {
         if (len > dll_path_maxlen) dll_path_maxlen = len;
         dll_paths[nb_dll_paths++] = dlldir;
+    }
+
+    if (dynamic_dlldir && (len = strlen(dynamic_dlldir))) {
+	if (len > dll_path_maxlen) dll_path_maxlen = len;
+	dll_paths[nb_dll_paths++] = dynamic_dlldir;
     }
 }
 
--- /dev/null	2003-09-15 14:40:47.000000000 +0100
+++ libs/wine/prefix.c	2004-01-22 19:33:59.000000000 +0000
@@ -0,0 +1,127 @@
+/*
+ * Dynamic DLL path detection support
+ *
+ * Copyright 2004 Mike Hearn
+ * Copyright 2004 Hongli Lai
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _PREFIX_C_
+#define _PREFIX_C_
+
+#include "config.h"
+
+#define __USE_GNU
+#include <features.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifdef ENABLE_BINRELOC
+#define __USE_GNU
+#include <dlfcn.h>
+#include <link.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+
+/**
+ * wine_locate:
+ * symbol: A symbol that belongs to the app/library you want to locate.
+ * Returns: A newly allocated string containing the full path of the
+ *	    app/library that func belongs to, or NULL on error. This
+ *	    string should be freed when not when no longer needed.
+ *
+ * Finds out to which application or library symbol belongs, then locate
+ * the full path of that application or library.
+ * Note that symbol cannot be a pointer to a function. That will not work.
+ *
+ * Example:
+ * // main.c
+ * #include "prefix.h"
+ * #include "libfoo.h"
+ *
+ * int main (int argc, char *argv[]) {
+ *	printf ("Full path of this app: %s\n", wine_locate (&argc));
+ *	libfoo_start ();
+ *	return 0;
+ * }
+ *
+ * // libfoo.c starts here
+ * #include "prefix.h"
+ *
+ * void libfoo_start () {
+ *	// "" is a symbol that belongs to libfoo (because it's called
+ *	// from libfoo_start()); that's why this works.
+ *	printf ("libfoo is located in: %s\n", wine_locate (""));
+ * }
+ */
+char *
+wine_locate (void *symbol)
+{
+	Dl_info info;
+	char line[5000];
+	FILE *f;
+	char *path;
+
+	if (symbol == NULL) return NULL;
+	if (!dladdr (symbol, &info)) return NULL;
+	
+	f = fopen ("/proc/self/maps", "r");
+	if (f == NULL) return NULL;
+
+	while (fgets (line, sizeof (line), f))
+	{
+		unsigned int start;
+
+		sscanf (line, "%x-", &start);
+
+		if (start == (unsigned int) info.dli_fbase)
+		{
+			char *tmp;
+			size_t len;
+
+			/* Extract the filename; it is always an absolute path */
+			path = strchr (line, '/');
+
+			/* Get rid of the newline */
+			tmp = strrchr (path, '\n');
+			if (tmp) *tmp = 0;
+
+			/* Get rid of "(deleted)" */
+			len = strlen (path);
+			if (len > 10 && strcmp (path + len - 10, " (deleted)") == 0)
+			{
+				tmp = path + len - 10;
+				*tmp = 0;
+			}
+
+			fclose(f);
+			return strdup (path);
+		}
+	}
+
+	fclose (f);
+	/* we should not get here */
+	return NULL;
+}
+
+
+#endif /* ENABLE_BINRELOC */
+
+#endif /* _PREFIX_C */





More information about the wine-patches mailing list