Kernel 2.4 / glibc 2.2.X build fixes for recent startup changes.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@259 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c
index 015b9f7..df89517 100644
--- a/coregrind/arch/x86-linux/vg_libpthread.c
+++ b/coregrind/arch/x86-linux/vg_libpthread.c
@@ -104,8 +104,6 @@
 static __inline__
 void ensure_valgrind ( char* caller )
 {
-   char* str;
-   int is_valgrind; 
    VG_(startup)();
 }
 
@@ -333,7 +331,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1    
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          attr->__mutexkind = type;
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index f280ef8..d4622b2 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -627,10 +627,22 @@
 extern Int     VG_(longjmpd_on_signal);
 
 
-/* We check that the initial stack, which we can't move, is allocated
-   here.  VG_(scheduler_init) checks this.  
+/* Possible places where the main stack might be based.  We check that
+   the initial stack, which we can't move, is allocated here.
+   VG_(scheduler_init) checks this.  Andrea Archelangi's 2.4 kernels
+   have been rumoured to start stacks at 0x80000000, so that too is
+   considered.  
 */
-#define VG_STARTUP_STACK_MASK  (Addr)0xBFF80000
+#define VG_STARTUP_STACK_BASE_1  (Addr)0xC0000000
+#define VG_STARTUP_STACK_BASE_2  (Addr)0x80000000
+#define VG_STARTUP_STACK_SMALLERTHAN  0x100000 /* 1024k */
+
+#define VG_STACK_MATCHES_BASE(zzstack, zzbase)                 \
+   (                                                           \
+      ((zzstack) & ((zzbase) - VG_STARTUP_STACK_SMALLERTHAN))  \
+      ==                                                       \
+      ((zzbase) - VG_STARTUP_STACK_SMALLERTHAN)                \
+   )
 
 
 /* The red-zone size which we put at the bottom (highest address) of
diff --git a/coregrind/vg_kerneliface.h b/coregrind/vg_kerneliface.h
index 362a553..a7690dd 100644
--- a/coregrind/vg_kerneliface.h
+++ b/coregrind/vg_kerneliface.h
@@ -297,6 +297,24 @@
 };
 
 
+/* To do with the ELF constructed by the kernel on a process' stack
+   just before it transfers control to the program's interpreter
+   (to use the ELF parlance).  
+   Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h
+   Logic from     /usr/src/linux-2.4.9-31/fs/binfmt_elf.c
+                  and its counterpart in the 2.2.14 kernel sources 
+                  in Red Hat 6.2.
+*/
+#define VKI_AT_CLKTCK 17    /* frequency at which times() increments */
+#define VKI_AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
+#define VKI_AT_BASE   7     /* base address of interpreter */
+#define VKI_AT_PAGESZ 6     /* system page size */
+#define VKI_AT_PHNUM  5     /* number of program headers */
+#define VKI_AT_PHENT  4     /* size of program header entry */
+#define VKI_AT_PHDR   3     /* program headers for program */
+
+
+
 #endif /* ndef __VG_KERNELIFACE_H */
 
 /*--------------------------------------------------------------------*/
diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c
index 015b9f7..df89517 100644
--- a/coregrind/vg_libpthread.c
+++ b/coregrind/vg_libpthread.c
@@ -104,8 +104,6 @@
 static __inline__
 void ensure_valgrind ( char* caller )
 {
-   char* str;
-   int is_valgrind; 
    VG_(startup)();
 }
 
@@ -333,7 +331,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1    
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          attr->__mutexkind = type;
diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c
index 6df3e92..330068d 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/vg_main.c
@@ -481,6 +481,15 @@
    VG_(exit)(1);
 }
 
+static void args_grok_error ( Char* msg )
+{
+   VG_(shutdown_logging)();
+   VG_(clo_logfile_fd) = 2; /* stderr */
+   VG_(printf)("valgrind.so: When searching for "
+               "client's argc/argc/envp:\n\t%s\n", msg);
+   config_error("couldn't find client's argc/argc/envp");
+}   
+
 
 static void process_cmd_line_options ( void )
 {
@@ -488,7 +497,7 @@
    UInt   argc;
    UChar* p;
    UChar* str;
-   Int    i, eventually_logfile_fd;
+   Int    i, eventually_logfile_fd, ctr;
 
 #  define ISSPACE(cc)      ((cc) == ' ' || (cc) == '\t' || (cc) == '\n')
 #  define STREQ(s1,s2)     (0==VG_(strcmp_ws)((s1),(s2)))
@@ -532,161 +541,89 @@
    VG_(startup_logging)();
 
 
-   /* We look for the Linux ELF table and go down until we find the
+   /* (Suggested by Fabrice Bellard ... )
+      We look for the Linux ELF table and go down until we find the
       envc & envp. It is not full proof, but these structures should
       change less often than the libc ones. */
    {
-       unsigned long *sp;
-       int i;
+       UInt* sp = 0; /* bogus init to keep gcc -O happy */
+
        /* locate the top of the stack */
-       sp = (unsigned long *)(((unsigned long)VG_(esp_at_startup) & 
-                               0xF0000000) + 0x10000000);
+       if (VG_STACK_MATCHES_BASE( VG_(esp_at_startup), 
+                                  VG_STARTUP_STACK_BASE_1 )) {
+          sp = (UInt*)VG_STARTUP_STACK_BASE_1;
+       } else
+       if (VG_STACK_MATCHES_BASE( VG_(esp_at_startup), 
+                                  VG_STARTUP_STACK_BASE_2 )) {
+          sp = (UInt*)VG_STARTUP_STACK_BASE_2;
+       } else {
+          args_grok_error(
+             "startup %esp is not near any VG_STARTUP_STACK_BASE_*\n   "
+             "constants defined in vg_include.h.  You should investigate."
+          );
+       }
+ 
        /* we locate: NEW_AUX_ENT(1, AT_PAGESZ, ELF_EXEC_PAGESIZE) in
           the elf interpreter table */
        sp -= 2;
-
-#define VKI_AT_PAGESZ 6
-
        while (sp[0] != VKI_AT_PAGESZ || sp[1] != 4096) {
-	 /*  VG_(printf)("trying %p\n", sp); */
+           /* VG_(printf)("trying %p\n", sp); */
            sp--;
        }
-#define VKI_AT_BASE   7     /* base address of interpreter */
-#define VKI_AT_PAGESZ 6     /* system page size */
-#define VKI_AT_PHNUM  5     /* number of program headers */
-#define VKI_AT_PHENT  4     /* size of program header entry */
-#define VKI_AT_PHDR   3     /* program headers for program */
 
        if (sp[2] == VKI_AT_BASE 
            && sp[0] == VKI_AT_PAGESZ
            && sp[-2] == VKI_AT_PHNUM
            && sp[-4] == VKI_AT_PHENT
            && sp[-6] == VKI_AT_PHDR) {
-          VG_(printf)("Looks like you've got a 2.2.X kernel here.\n");
+          if (0)
+             VG_(printf)("Looks like you've got a 2.2.X kernel here.\n");
           sp -= 6;
-       }
+       } else
+       if (sp[2] == VKI_AT_CLKTCK
+           && sp[0] == VKI_AT_PAGESZ
+           && sp[-2] == VKI_AT_HWCAP) {
+          if (0)
+             VG_(printf)("Looks like you've got a 2.4.X kernel here.\n");
+          sp -= 2;
+       } else
+         args_grok_error(
+            "ELF frame does not look like 2.2.X or 2.4.X.\n   "
+            "See kernel sources linux/fs/binfmt_elf.c to make sense of this."
+         );
 
        sp--;
-       vg_assert(*sp == 0);
+       if (*sp != 0)
+	 args_grok_error("can't find NULL at end of env[]");
+
        /* sp now points to NULL at the end of env[] */
+       ctr = 0;
        while (True) {
            sp --;
            if (*sp == 0) break;
+           if (++ctr >= 1000)
+              args_grok_error(
+                 "suspiciously many (1000) env[] entries; giving up");
+           
        }
        /* sp now points to NULL at the end of argv[] */
-       VG_(client_envp) = sp+1;
+       VG_(client_envp) = (Char**)(sp+1);
 
+       ctr = 0;
        VG_(client_argc) = 0;
        while (True) {
           sp--;
           if (*sp == VG_(client_argc))
              break;
           VG_(client_argc)++;
+           if (++ctr >= 1000)
+              args_grok_error(
+                 "suspiciously many (1000) argv[] entries; giving up");
        }
 
-       VG_(client_argv) = sp+1;
+       VG_(client_argv) = (Char**)(sp+1);
    }
 
-
-#if 0
-   /* Magically find the client's argc/argv/envp.  This kludge is
-      entirely dependent on the stack layout imposed by libc at
-      startup.  Hence the magic offsets.  Then check (heuristically)
-      that the results are plausible.  There must be a better way to
-      do this ... */
-
-#  if 1
-   /* Use this to search for the correct offsets if the tests below
-      barf. */
-   { Int i;
-     VG_(printf)("startup %%esp is %p\n", VG_(esp_at_startup) );
-     for (i = -10; i < 20; i++) {
-        Char* p = ((Char**)VG_(esp_at_startup))[i];
-        VG_(printf)("%d:  %p\n", i, p);
-     }
-   }
-#  endif
-
-#  if defined(GLIBC_2_2)
-   /* These offsets (5,6,7) are right for my RedHat 7.2 (glibc-2.2.4)
-      box. */
-
-   VG_(client_argc) = (Int)   ( ((void**)VG_(esp_at_startup)) [5] );
-   VG_(client_argv) = (Char**)( ((void**)VG_(esp_at_startup)) [6] );
-   VG_(client_envp) = (Char**)( ((void**)VG_(esp_at_startup)) [7] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-
-   /* If that's no good, try some other offsets discovered by KDE
-      folks on 8 Feb 02:
-      For glibc > 2.2.4 the offset 9/10/11 did the trick. Coolo found
-      out those, on I think a Caldera 3.1 with glibc 2.2.4 -- the same
-      offsets worked for on a debian sid with glibc 2.2.5.  */
-
-   VG_(client_argc) = (Int)   ( ((void**)VG_(esp_at_startup)) [9] );
-   VG_(client_argv) = (Char**)( ((void**)VG_(esp_at_startup)) [10] );
-   VG_(client_envp) = (Char**)( ((void**)VG_(esp_at_startup)) [11] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-
-#  endif /* defined(GLIBC_2_2) */
-
-#  if defined(GLIBC_2_1)
-   /* Doesn't look promising.  Try offsets for RedHat 6.2
-      (glibc-2.1.3) instead.  In this case, the argv and envp vectors
-      are actually on the stack (bizarrely). */
-
-   VG_(client_argc) = (Int)      ( ((void**)VG_(esp_at_startup)) [4] );
-   VG_(client_argv) = (Char**) & ( ((void**)VG_(esp_at_startup)) [5] );
-   VG_(client_envp) 
-      = (Char**) & ( ((void**)VG_(esp_at_startup)) [6 + VG_(client_argc)] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-
-   /* Here's yet another variant, from <hansen> (irc.kde.org). */
-
-   VG_(client_argc) = (Int)      ( ((void**)VG_(esp_at_startup)) [9] );
-   VG_(client_argv) = (Char**) & ( ((void**)VG_(esp_at_startup)) [10] );
-   VG_(client_envp) 
-      = (Char**) & ( ((void**)VG_(esp_at_startup)) [11 + VG_(client_argc)] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-#  endif /* defined(GLIBC_2_1) */
-
-#  if !defined(GLIBC_2_2) && !defined(GLIBC_2_1)
-   config_error("autoconf/configure detected neither glibc 2.1.X nor 2.2.X");
-#  endif
-
-   /* VG_(printf)("%d %p %p\n", VG_(client_argc), VG_(client_argv), 
-                                                  VG_(client_envp));
-   */
-   /* We're hosed.  Give up :-( */
-   config_error(
-      "Can't get plausible values for client's argc/argv/envp.\n\t"
-      "You may be able to fix this; see process_cmd_line_options()\n\t"
-      "in vg_main.c"
-   );
-   /* NOTREACHED */
-
-  argc_argv_envp_OK:
-#endif
-
    /* Now that VG_(client_envp) has been set, we can extract the args
       for Valgrind itself.  Copy into global var so that we don't have to
       write zeroes to the getenv'd value itself. */
diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c
index 54dc30b..79056e7 100644
--- a/coregrind/vg_scheduler.c
+++ b/coregrind/vg_scheduler.c
@@ -524,9 +524,15 @@
    ThreadId tid_main;
 
    startup_esp = VG_(baseBlock)[VGOFF_(m_esp)];
-   if ((startup_esp & VG_STARTUP_STACK_MASK) != VG_STARTUP_STACK_MASK) {
-      VG_(printf)("%%esp at startup = %p is not near %p; aborting\n", 
-                  (void*)startup_esp, (void*)VG_STARTUP_STACK_MASK);
+
+   if (VG_STACK_MATCHES_BASE(startup_esp, VG_STARTUP_STACK_BASE_1)
+       || VG_STACK_MATCHES_BASE(startup_esp, VG_STARTUP_STACK_BASE_2)) {
+      /* Jolly good! */
+   } else {
+      VG_(printf)("%%esp at startup = %p is not near %p or %p; aborting\n", 
+                  (void*)startup_esp, 
+                  (void*)VG_STARTUP_STACK_BASE_1,
+                  (void*)VG_STARTUP_STACK_BASE_2 );
       VG_(panic)("unexpected %esp at startup");
    }
 
@@ -1922,7 +1928,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          if (mutex->__m_count >= 0) break;
@@ -2016,7 +2024,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          if (mutex->__m_count >= 0) break;
@@ -2234,7 +2244,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          if (mutex->__m_count >= 0) break;
diff --git a/vg_include.h b/vg_include.h
index f280ef8..d4622b2 100644
--- a/vg_include.h
+++ b/vg_include.h
@@ -627,10 +627,22 @@
 extern Int     VG_(longjmpd_on_signal);
 
 
-/* We check that the initial stack, which we can't move, is allocated
-   here.  VG_(scheduler_init) checks this.  
+/* Possible places where the main stack might be based.  We check that
+   the initial stack, which we can't move, is allocated here.
+   VG_(scheduler_init) checks this.  Andrea Archelangi's 2.4 kernels
+   have been rumoured to start stacks at 0x80000000, so that too is
+   considered.  
 */
-#define VG_STARTUP_STACK_MASK  (Addr)0xBFF80000
+#define VG_STARTUP_STACK_BASE_1  (Addr)0xC0000000
+#define VG_STARTUP_STACK_BASE_2  (Addr)0x80000000
+#define VG_STARTUP_STACK_SMALLERTHAN  0x100000 /* 1024k */
+
+#define VG_STACK_MATCHES_BASE(zzstack, zzbase)                 \
+   (                                                           \
+      ((zzstack) & ((zzbase) - VG_STARTUP_STACK_SMALLERTHAN))  \
+      ==                                                       \
+      ((zzbase) - VG_STARTUP_STACK_SMALLERTHAN)                \
+   )
 
 
 /* The red-zone size which we put at the bottom (highest address) of
diff --git a/vg_kerneliface.h b/vg_kerneliface.h
index 362a553..a7690dd 100644
--- a/vg_kerneliface.h
+++ b/vg_kerneliface.h
@@ -297,6 +297,24 @@
 };
 
 
+/* To do with the ELF constructed by the kernel on a process' stack
+   just before it transfers control to the program's interpreter
+   (to use the ELF parlance).  
+   Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h
+   Logic from     /usr/src/linux-2.4.9-31/fs/binfmt_elf.c
+                  and its counterpart in the 2.2.14 kernel sources 
+                  in Red Hat 6.2.
+*/
+#define VKI_AT_CLKTCK 17    /* frequency at which times() increments */
+#define VKI_AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
+#define VKI_AT_BASE   7     /* base address of interpreter */
+#define VKI_AT_PAGESZ 6     /* system page size */
+#define VKI_AT_PHNUM  5     /* number of program headers */
+#define VKI_AT_PHENT  4     /* size of program header entry */
+#define VKI_AT_PHDR   3     /* program headers for program */
+
+
+
 #endif /* ndef __VG_KERNELIFACE_H */
 
 /*--------------------------------------------------------------------*/
diff --git a/vg_libpthread.c b/vg_libpthread.c
index 015b9f7..df89517 100644
--- a/vg_libpthread.c
+++ b/vg_libpthread.c
@@ -104,8 +104,6 @@
 static __inline__
 void ensure_valgrind ( char* caller )
 {
-   char* str;
-   int is_valgrind; 
    VG_(startup)();
 }
 
@@ -333,7 +331,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1    
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          attr->__mutexkind = type;
diff --git a/vg_main.c b/vg_main.c
index 6df3e92..330068d 100644
--- a/vg_main.c
+++ b/vg_main.c
@@ -481,6 +481,15 @@
    VG_(exit)(1);
 }
 
+static void args_grok_error ( Char* msg )
+{
+   VG_(shutdown_logging)();
+   VG_(clo_logfile_fd) = 2; /* stderr */
+   VG_(printf)("valgrind.so: When searching for "
+               "client's argc/argc/envp:\n\t%s\n", msg);
+   config_error("couldn't find client's argc/argc/envp");
+}   
+
 
 static void process_cmd_line_options ( void )
 {
@@ -488,7 +497,7 @@
    UInt   argc;
    UChar* p;
    UChar* str;
-   Int    i, eventually_logfile_fd;
+   Int    i, eventually_logfile_fd, ctr;
 
 #  define ISSPACE(cc)      ((cc) == ' ' || (cc) == '\t' || (cc) == '\n')
 #  define STREQ(s1,s2)     (0==VG_(strcmp_ws)((s1),(s2)))
@@ -532,161 +541,89 @@
    VG_(startup_logging)();
 
 
-   /* We look for the Linux ELF table and go down until we find the
+   /* (Suggested by Fabrice Bellard ... )
+      We look for the Linux ELF table and go down until we find the
       envc & envp. It is not full proof, but these structures should
       change less often than the libc ones. */
    {
-       unsigned long *sp;
-       int i;
+       UInt* sp = 0; /* bogus init to keep gcc -O happy */
+
        /* locate the top of the stack */
-       sp = (unsigned long *)(((unsigned long)VG_(esp_at_startup) & 
-                               0xF0000000) + 0x10000000);
+       if (VG_STACK_MATCHES_BASE( VG_(esp_at_startup), 
+                                  VG_STARTUP_STACK_BASE_1 )) {
+          sp = (UInt*)VG_STARTUP_STACK_BASE_1;
+       } else
+       if (VG_STACK_MATCHES_BASE( VG_(esp_at_startup), 
+                                  VG_STARTUP_STACK_BASE_2 )) {
+          sp = (UInt*)VG_STARTUP_STACK_BASE_2;
+       } else {
+          args_grok_error(
+             "startup %esp is not near any VG_STARTUP_STACK_BASE_*\n   "
+             "constants defined in vg_include.h.  You should investigate."
+          );
+       }
+ 
        /* we locate: NEW_AUX_ENT(1, AT_PAGESZ, ELF_EXEC_PAGESIZE) in
           the elf interpreter table */
        sp -= 2;
-
-#define VKI_AT_PAGESZ 6
-
        while (sp[0] != VKI_AT_PAGESZ || sp[1] != 4096) {
-	 /*  VG_(printf)("trying %p\n", sp); */
+           /* VG_(printf)("trying %p\n", sp); */
            sp--;
        }
-#define VKI_AT_BASE   7     /* base address of interpreter */
-#define VKI_AT_PAGESZ 6     /* system page size */
-#define VKI_AT_PHNUM  5     /* number of program headers */
-#define VKI_AT_PHENT  4     /* size of program header entry */
-#define VKI_AT_PHDR   3     /* program headers for program */
 
        if (sp[2] == VKI_AT_BASE 
            && sp[0] == VKI_AT_PAGESZ
            && sp[-2] == VKI_AT_PHNUM
            && sp[-4] == VKI_AT_PHENT
            && sp[-6] == VKI_AT_PHDR) {
-          VG_(printf)("Looks like you've got a 2.2.X kernel here.\n");
+          if (0)
+             VG_(printf)("Looks like you've got a 2.2.X kernel here.\n");
           sp -= 6;
-       }
+       } else
+       if (sp[2] == VKI_AT_CLKTCK
+           && sp[0] == VKI_AT_PAGESZ
+           && sp[-2] == VKI_AT_HWCAP) {
+          if (0)
+             VG_(printf)("Looks like you've got a 2.4.X kernel here.\n");
+          sp -= 2;
+       } else
+         args_grok_error(
+            "ELF frame does not look like 2.2.X or 2.4.X.\n   "
+            "See kernel sources linux/fs/binfmt_elf.c to make sense of this."
+         );
 
        sp--;
-       vg_assert(*sp == 0);
+       if (*sp != 0)
+	 args_grok_error("can't find NULL at end of env[]");
+
        /* sp now points to NULL at the end of env[] */
+       ctr = 0;
        while (True) {
            sp --;
            if (*sp == 0) break;
+           if (++ctr >= 1000)
+              args_grok_error(
+                 "suspiciously many (1000) env[] entries; giving up");
+           
        }
        /* sp now points to NULL at the end of argv[] */
-       VG_(client_envp) = sp+1;
+       VG_(client_envp) = (Char**)(sp+1);
 
+       ctr = 0;
        VG_(client_argc) = 0;
        while (True) {
           sp--;
           if (*sp == VG_(client_argc))
              break;
           VG_(client_argc)++;
+           if (++ctr >= 1000)
+              args_grok_error(
+                 "suspiciously many (1000) argv[] entries; giving up");
        }
 
-       VG_(client_argv) = sp+1;
+       VG_(client_argv) = (Char**)(sp+1);
    }
 
-
-#if 0
-   /* Magically find the client's argc/argv/envp.  This kludge is
-      entirely dependent on the stack layout imposed by libc at
-      startup.  Hence the magic offsets.  Then check (heuristically)
-      that the results are plausible.  There must be a better way to
-      do this ... */
-
-#  if 1
-   /* Use this to search for the correct offsets if the tests below
-      barf. */
-   { Int i;
-     VG_(printf)("startup %%esp is %p\n", VG_(esp_at_startup) );
-     for (i = -10; i < 20; i++) {
-        Char* p = ((Char**)VG_(esp_at_startup))[i];
-        VG_(printf)("%d:  %p\n", i, p);
-     }
-   }
-#  endif
-
-#  if defined(GLIBC_2_2)
-   /* These offsets (5,6,7) are right for my RedHat 7.2 (glibc-2.2.4)
-      box. */
-
-   VG_(client_argc) = (Int)   ( ((void**)VG_(esp_at_startup)) [5] );
-   VG_(client_argv) = (Char**)( ((void**)VG_(esp_at_startup)) [6] );
-   VG_(client_envp) = (Char**)( ((void**)VG_(esp_at_startup)) [7] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-
-   /* If that's no good, try some other offsets discovered by KDE
-      folks on 8 Feb 02:
-      For glibc > 2.2.4 the offset 9/10/11 did the trick. Coolo found
-      out those, on I think a Caldera 3.1 with glibc 2.2.4 -- the same
-      offsets worked for on a debian sid with glibc 2.2.5.  */
-
-   VG_(client_argc) = (Int)   ( ((void**)VG_(esp_at_startup)) [9] );
-   VG_(client_argv) = (Char**)( ((void**)VG_(esp_at_startup)) [10] );
-   VG_(client_envp) = (Char**)( ((void**)VG_(esp_at_startup)) [11] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-
-#  endif /* defined(GLIBC_2_2) */
-
-#  if defined(GLIBC_2_1)
-   /* Doesn't look promising.  Try offsets for RedHat 6.2
-      (glibc-2.1.3) instead.  In this case, the argv and envp vectors
-      are actually on the stack (bizarrely). */
-
-   VG_(client_argc) = (Int)      ( ((void**)VG_(esp_at_startup)) [4] );
-   VG_(client_argv) = (Char**) & ( ((void**)VG_(esp_at_startup)) [5] );
-   VG_(client_envp) 
-      = (Char**) & ( ((void**)VG_(esp_at_startup)) [6 + VG_(client_argc)] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-
-   /* Here's yet another variant, from <hansen> (irc.kde.org). */
-
-   VG_(client_argc) = (Int)      ( ((void**)VG_(esp_at_startup)) [9] );
-   VG_(client_argv) = (Char**) & ( ((void**)VG_(esp_at_startup)) [10] );
-   VG_(client_envp) 
-      = (Char**) & ( ((void**)VG_(esp_at_startup)) [11 + VG_(client_argc)] );
-
-   if ( ((UInt)VG_(client_argc)) > 0 &&
-        ((UInt)VG_(client_argc)) < 10000 &&
-        (Addr)VG_(client_argv) >= 0x8000000 &&
-        (Addr)VG_(client_envp) >= 0x8000000)
-      goto argc_argv_envp_OK;
-#  endif /* defined(GLIBC_2_1) */
-
-#  if !defined(GLIBC_2_2) && !defined(GLIBC_2_1)
-   config_error("autoconf/configure detected neither glibc 2.1.X nor 2.2.X");
-#  endif
-
-   /* VG_(printf)("%d %p %p\n", VG_(client_argc), VG_(client_argv), 
-                                                  VG_(client_envp));
-   */
-   /* We're hosed.  Give up :-( */
-   config_error(
-      "Can't get plausible values for client's argc/argv/envp.\n\t"
-      "You may be able to fix this; see process_cmd_line_options()\n\t"
-      "in vg_main.c"
-   );
-   /* NOTREACHED */
-
-  argc_argv_envp_OK:
-#endif
-
    /* Now that VG_(client_envp) has been set, we can extract the args
       for Valgrind itself.  Copy into global var so that we don't have to
       write zeroes to the getenv'd value itself. */
diff --git a/vg_scheduler.c b/vg_scheduler.c
index 54dc30b..79056e7 100644
--- a/vg_scheduler.c
+++ b/vg_scheduler.c
@@ -524,9 +524,15 @@
    ThreadId tid_main;
 
    startup_esp = VG_(baseBlock)[VGOFF_(m_esp)];
-   if ((startup_esp & VG_STARTUP_STACK_MASK) != VG_STARTUP_STACK_MASK) {
-      VG_(printf)("%%esp at startup = %p is not near %p; aborting\n", 
-                  (void*)startup_esp, (void*)VG_STARTUP_STACK_MASK);
+
+   if (VG_STACK_MATCHES_BASE(startup_esp, VG_STARTUP_STACK_BASE_1)
+       || VG_STACK_MATCHES_BASE(startup_esp, VG_STARTUP_STACK_BASE_2)) {
+      /* Jolly good! */
+   } else {
+      VG_(printf)("%%esp at startup = %p is not near %p or %p; aborting\n", 
+                  (void*)startup_esp, 
+                  (void*)VG_STARTUP_STACK_BASE_1,
+                  (void*)VG_STARTUP_STACK_BASE_2 );
       VG_(panic)("unexpected %esp at startup");
    }
 
@@ -1922,7 +1928,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          if (mutex->__m_count >= 0) break;
@@ -2016,7 +2024,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          if (mutex->__m_count >= 0) break;
@@ -2234,7 +2244,9 @@
       case PTHREAD_MUTEX_TIMED_NP:
       case PTHREAD_MUTEX_ADAPTIVE_NP:
 #     endif
+#     ifdef GLIBC_2_1
       case PTHREAD_MUTEX_FAST_NP:
+#     endif
       case PTHREAD_MUTEX_RECURSIVE_NP:
       case PTHREAD_MUTEX_ERRORCHECK_NP:
          if (mutex->__m_count >= 0) break;