PATCH: glibc 2.3.x and errno

Marcus Meissner meissner at suse.de
Fri Jan 24 10:20:12 CST 2003


Hi,

Beginning of this week, a colleague of mine walked up to me and said
"Wine does not work.".
Since this happens sometimes, I just walked to his office and
checked it for myself. And yes, it did not work. It reported:

	wineserver: lstat /tmp/.wine-coolo : No such file or directory

Some straces later the problem became apparent:

He is using a glibc 2.3 headbranch snapshot (the one we will ship in SuSE
8.2). Looking at the output of "nm", then looking at the sourcecode
confirmed first suspicions.

__errno_location and __h_errno_location are no longer weak symbols
and so can not be overwritten any longer. The internal glibc systemcall
wrappers no longer call the functions by reference, but directly.

Investigations and several talks to one of our glibc gurus (Andreas Schwab)
gave following results:
- This is intended behaviour and it will not be changed back.
- They are not meant to be overwritten.
- The functions are provided by the pthread implementation with which
  glibc was compiled. (either linuxthreads, ngpt, nptl or none).
  These are optimized for speed and do not support hooking.

I spend 2 days studying glibc and thinking on how to best solve this
problem, to no avail:
- Hooking into those functions is not possible, any approach
  will most likely not be accepted by the glibc hackers.
- Using dietlibc or uClibc is not possible, since dietlibc does not
  support shared libraries yet and for both C libraries libX11 and others
  would need to be compiled with them too.

The afore mentioned glibc guru did not have any ideas either.

There however is a happy but messy end, which appeared to me 
yesterday:
	Overwrite those 2 functions with a jump to our implementations.

So I did. You can now mark me up for the dark side.

Rerun autoconf and autoheader -I include.

Ciao, Marcus

Copyright: LGPL, X11 (just this once).

Changelog:
	Since glibc 2.3.cvs no longer exports __errno_location, we need
	to overwrite the implementation in glibc/libpthread directly.

Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.126
diff -u -r1.126 configure.ac
--- configure.ac	24 Jan 2003 01:08:15 -0000	1.126
+++ configure.ac	24 Jan 2003 16:13:52 -0000
@@ -852,10 +852,33 @@
         WINE_CHECK_ERRNO([__errno], [wine_cv_libc_reentrant=__errno])
 ))))
 
+
 if test "$wine_cv_libc_reentrant" != "no"
 then
   AC_DEFINE_UNQUOTED(ERRNO_LOCATION,$wine_cv_libc_reentrant,
 	             [Define to the name of the function returning errno for reentrant libc])
+fi
+
+dnl **** Check for non-weak __errno_location
+dnl This happens with glibc > 2.3.1.
+if test "$wine_cv_libc_reentrant" = "__errno_location"
+then
+  AC_CACHE_CHECK([for weak symbol __errno_location],wine_cv_errnolocation_weak,[
+    if nm /lib/libc.so.* | grep -i w.*__errno_location > /dev/null 2>&1 
+    then
+      wine_cv_errnolocation_weak=yes
+    else
+      wine_cv_errnolocation_weak=no
+    fi
+  ])
+else
+  wine_cv_errnolocation_weak=yes
+fi
+
+if test "$wine_cv_errnolocation_weak" != "no"
+then
+  AC_DEFINE_UNQUOTED(WEAK_ERRNO_LOCATION,1,
+   [Define if __errno_location is weak (can be overload by us)])
 fi
 
 dnl **** Check for reentrant X libraries ****
Index: include/wine/library.h
===================================================================
RCS file: /home/wine/wine/include/wine/library.h,v
retrieving revision 1.18
diff -u -r1.18 library.h
--- include/wine/library.h	15 Jan 2003 00:44:00 -0000	1.18
+++ include/wine/library.h	24 Jan 2003 16:14:15 -0000
@@ -65,6 +65,7 @@
 /* errno support */
 extern int* (*wine_errno_location)(void);
 extern int* (*wine_h_errno_location)(void);
+extern void fixup_errno_locations(void);
 
 /* LDT management */
 
Index: library/errno.c
===================================================================
RCS file: /home/wine/wine/library/errno.c,v
retrieving revision 1.3
diff -u -r1.3 errno.c
--- library/errno.c	8 May 2002 00:41:43 -0000	1.3
+++ library/errno.c	24 Jan 2003 16:14:15 -0000
@@ -19,6 +19,13 @@
  */
 
 #include "config.h"
+#include "wine/port.h"
+#include "wine/library.h"
+
+#include <stdio.h>
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
 
 /* default errno before threading is initialized */
 static int *default_errno_location(void)
@@ -37,6 +44,7 @@
 int* (*wine_errno_location)(void) = default_errno_location;
 int* (*wine_h_errno_location)(void) = default_h_errno_location;
 
+#ifdef WEAK_ERRNO_LOCATION
 /***********************************************************************
  *           __errno_location/__error/___errno
  *
@@ -58,3 +66,49 @@
 {
     return wine_h_errno_location();
 }
+
+void fixup_errno_locations(void) {
+	/* EMPTY */
+}
+#else
+
+/* Since newer glibc versions no longer export __errno_location and
+ * __h_errno_location as a weak symbol, we can no longer overwrite it.
+ *
+ * Since there is also no way to hook those functions, we can only
+ * overwrite the instructions for those functions, which we do below.
+ */
+
+#ifdef __i386__
+static inline void
+__writejump(unsigned char *addr, unsigned char *to) {
+    if (!addr) return;
+    /* Write a relative jump at the function address, but only if
+     * it is needed.
+     */
+    if ((addr[0] != 0xe9) || (*(DWORD*)(addr+1) != to-(addr+5))) {
+	    mprotect((void*)((unsigned int)addr & ~(getpagesize()-1)), 5, PROT_READ|PROT_EXEC|PROT_WRITE);
+	    addr[0] = 0xe9;
+	    *(DWORD*)(addr+1) = (unsigned char*)to-(addr+5);
+	    mprotect((void*)((unsigned int)addr & ~(getpagesize()-1)), 5, PROT_READ|PROT_EXEC);
+    }
+}
+#else
+#error "Implement jump writer for your architecture!"
+#endif
+
+void fixup_errno_locations(void)
+{
+    char		err[200];
+    unsigned char	*funp;
+
+    funp=wine_dlsym(RTLD_DEFAULT,"__errno_location",err,sizeof(err));
+    __writejump(funp, (unsigned char*)wine_errno_location);
+    funp=wine_dlsym(RTLD_NEXT,"__errno_location",err,sizeof(err));
+    __writejump(funp, (unsigned char*)wine_errno_location);
+    funp=wine_dlsym(RTLD_DEFAULT,"__h_errno_location",err,sizeof(err));
+    __writejump(funp, (unsigned char*)wine_h_errno_location);
+    funp=wine_dlsym(RTLD_NEXT,"__h_errno_location",err,sizeof(err));
+    __writejump(funp, (unsigned char*)wine_h_errno_location);
+}
+#endif
Index: library/port.c
===================================================================
RCS file: /home/wine/wine/library/port.c,v
retrieving revision 1.39
diff -u -r1.39 port.c
--- library/port.c	13 Dec 2002 23:30:54 -0000	1.39
+++ library/port.c	24 Jan 2003 16:14:23 -0000
@@ -521,6 +521,10 @@
 	error[errorsize - 1] = '\0';
     }
     dlerror();
+    /* Check for new __errno_location functions, which might have
+     * come from a -lpthread link (like libGL.so). Overwrite them too.
+     */
+    fixup_errno_locations();
     return ret;
 #else
     if (error)
Index: miscemu/main.c
===================================================================
RCS file: /home/wine/wine/miscemu/main.c,v
retrieving revision 1.61
diff -u -r1.61 main.c
--- miscemu/main.c	9 Jan 2003 01:57:15 -0000	1.61
+++ miscemu/main.c	24 Jan 2003 16:14:23 -0000
@@ -99,6 +107,7 @@
  */
 int main( int argc, char *argv[] )
 {
+    fixup_errno_locations(); /* change __errno_location handlers in glibc now */
     PROCESS_InitWine( argc, argv, main_exe_name, &main_exe_file );
     return 1;  /* not reached */
 }
Index: scheduler/thread.c
===================================================================
RCS file: /home/wine/wine/scheduler/thread.c,v
retrieving revision 1.128
diff -u -r1.128 thread.c
--- scheduler/thread.c	5 Dec 2002 19:56:15 -0000	1.128
+++ scheduler/thread.c	24 Jan 2003 16:14:23 -0000
@@ -247,6 +247,11 @@
         SYSDEPS_SetCurThread( &initial_teb );
         wine_errno_location = thread_errno_location;
         wine_h_errno_location = thread_h_errno_location;
+
+	/* Fixup glibc and pthread __errno_location handlers with
+	 * new __errno_location jump targets.
+	 */
+	fixup_errno_locations();
     }
 }
 



More information about the wine-patches mailing list