Here's a program that demonstrates the actual issue at hand.
Basically, libgc finds the stack address from either glibc or the
kernel, which is not what it wants because valgrind messes with the
address space of the host program. This is only on Linux, but I
understand the issue is similar on other BSD systems.

This is the Mono file that contains some workarounds (specifically
mono/metadata/boehm-gc.c:mono_gc_base_init) . Contrary to the comments
in the file the issue does not appear to be thread-related, although
it is solved using pthread functionality.

http://anonsvn.mono-project.com/viewvc/trunk/mono/mono/metadata/boehm-gc.c?view=log

The attached program grabs the stack address from the kernel, glibc,
and by taking the address of a stack object, then prints the
difference. When run normally, the differences are minimal, but when
run under valgrind, there is a giant difference between the base of
the actual program and the base given by the kernel.

Ideally, this would be patched in libgc, but we'd need a way to detect
whether the program is running under valgrind and the current
workaround would only work for threaded versions. I'm going to take a
crack at libgc CVS later on.
/* demo valgrind program: cc -o demo demo.c */

#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define STAT_BUFFER_SIZE 4096
#define STAT_SKIP 27

extern ptrdiff_t __libc_stack_end;

int main(void) {
  int dummy;
  
  /* lifted from os_dep.c in libgc */
  char stat_buf[STAT_BUFFER_SIZE];
  int f = open("/proc/self/stat", O_RDONLY);
  if(f < 0 || read(f, stat_buf, STAT_BUFFER_SIZE) < 2 * STAT_SKIP) {
    perror("/proc/self/stat");
    return -1;
  }
  
  size_t buf_offset = 0;
  char c = stat_buf[buf_offset++];
  size_t i;
  for(i = 0; i < STAT_SKIP; ++i) {
    while(isspace(c)) c = stat_buf[buf_offset++];
    while(!isspace(c)) c = stat_buf[buf_offset++];
  }
  while(isspace(c)) c = stat_buf[buf_offset++];
  
  ptrdiff_t result = 0;
  while(isdigit(c)) {
    result *= 10;
    result += c - '0';
    c = stat_buf[buf_offset++];
  }
  close(f);
  
  printf("kernel stack base: %p\nlibc stack base: %p\ndumb stack base: %p\ndifference between kernel and dumb: %zd bytes\n",
         (void*) result, (void*) __libc_stack_end, (void*) &dummy, (void*) (result - (ptrdiff_t) &dummy));
}

Reply via email to