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/coregrind/vg_main.c b/coregrind/vg_main.c
index 06b6498..6df3e92 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/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
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