New and hopefully more reliable method for finding argc/argv/envp at
startup, by looking for the ELF frame created on the process' stack
at startup.  This avoids having to deal with problems caused by glibc
magic offsets.

WARNING: only works for 2.2 kernels right now.  2.4 is broken.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@258 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/vg_main.c b/vg_main.c
index 06b6498..6df3e92 100644
--- a/vg_main.c
+++ b/vg_main.c
@@ -531,18 +531,76 @@
       to failures in initialisation. */
    VG_(startup_logging)();
 
+
+   /* 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;
+       /* locate the top of the stack */
+       sp = (unsigned long *)(((unsigned long)VG_(esp_at_startup) & 
+                               0xF0000000) + 0x10000000);
+       /* 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); */
+           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");
+          sp -= 6;
+       }
+
+       sp--;
+       vg_assert(*sp == 0);
+       /* sp now points to NULL at the end of env[] */
+       while (True) {
+           sp --;
+           if (*sp == 0) break;
+       }
+       /* sp now points to NULL at the end of argv[] */
+       VG_(client_envp) = sp+1;
+
+       VG_(client_argc) = 0;
+       while (True) {
+          sp--;
+          if (*sp == VG_(client_argc))
+             break;
+          VG_(client_argc)++;
+       }
+
+       VG_(client_argv) = 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 0
+#  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 = 0; i < 10; i++) {
+     for (i = -10; i < 20; i++) {
         Char* p = ((Char**)VG_(esp_at_startup))[i];
         VG_(printf)("%d:  %p\n", i, p);
      }
@@ -627,6 +685,7 @@
    /* 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