text: /*
   * See the dyninst/COPYRIGHT file for copyright information.
   * 
   * We provide the Paradyn Tools (below described as "Paradyn")
   * on an AS IS basis, and do not warrant its validity or performance.
   * We reserve the right to update, modify, or discontinue this
   * software at any time.  We shall have no obligation to supply such
   * updates or modifications or any other form of support to you.
   * 
   * By your use of Paradyn, you understand and agree that we (or any
   * other person or entity with proprietary rights in Paradyn) are
   * under no obligation to provide either maintenance services,
   * update services, notices of latent defects, or correction of
   * defects for Paradyn.
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
  /************************************************************************
   * $Id: RTlinux.c,v 1.54 2008/04/11 23:30:44 legendre Exp $
   * RTlinux.c: mutatee-side library function specific to Linux
   ************************************************************************/
  
  #include "dyninstAPI_RT/h/dyninstAPI_RT.h"
  #include "dyninstAPI_RT/src/RTthread.h"
  #include "dyninstAPI_RT/src/RTcommon.h"
  #include <assert.h>
  #include <stdio.h>
  #include <errno.h>
  #include <unistd.h>
  
  #if !defined(DYNINST_RT_STATIC_LIB)
  #include <dlfcn.h>
  #endif
  
  #include <sys/types.h>
  #include <sys/syscall.h>
  #include <string.h>
  #include <errno.h>
  #include <signal.h>
  #include <sys/mman.h>
  #include <link.h>
  
  #if defined(DYNINST_RT_STATIC_LIB)
  /* 
   * The weak symbol here removes the dependence of the static version of this
   * library on pthread_self. If pthread_self is available, then it will be
   * linked.  Otherwise, the linker will ignore it.
   */
  #pragma weak pthread_self
  extern pthread_t pthread_self(void);
  #else
  #include <pthread.h>
  #endif
  
  extern double DYNINSTstaticHeap_512K_lowmemHeap_1[];
  extern double DYNINSTstaticHeap_16M_anyHeap_1[];
  extern unsigned long sizeOfLowMemHeap1;
  extern unsigned long sizeOfAnyHeap1;
  
  static struct trap_mapping_header *getStaticTrapMap(unsigned long addr);
  
  #if defined(arch_power) && defined(arch_64bit) && defined(os_linux)
  unsigned long DYNINSTlinkSave;
  unsigned long DYNINSTtocSave;
  #endif
  
  /************************************************************************
   * void DYNINSTbreakPoint(void)
   *
   * stop oneself.
  ************************************************************************/
  
  #ifndef SYS_tkill
  #define SYS_tkill 238
  #endif
  
  int t_kill(int pid, int sig) {
      static int has_tkill = 1;
      long int result = 0;
      if (has_tkill) {
          result = syscall(SYS_tkill, pid, sig);
          if (result == -1 && errno == ENOSYS) {
              has_tkill = 0;
          }
      }
      if (!has_tkill) {
          result = kill(pid, sig);
      }
  
      return (result == 0);
  }
  
  void DYNINSTbreakPoint()
  {
     /* We set a global flag here so that we can tell
        if we're ever in a call to this when we get a 
        SIGBUS */
     if (DYNINSTstaticMode)
        return;
  
     DYNINST_break_point_event = 1;
     while (DYNINST_break_point_event)  {
        t_kill(dyn_lwp_self(), DYNINST_BREAKPOINT_SIGNUM);
     }
     /* Mutator resets to 0... */
  }
  
  static int failed_breakpoint = 0;
  void uncaught_breakpoint(int sig)
  {
     (void)sig; /* unused parameter */
     failed_breakpoint = 1;
  }
  
  void DYNINSTlinuxBreakPoint()
  {
     struct sigaction act, oldact;
     int result;
     
     if (DYNINSTstaticMode)
        return;
  
     memset(&oldact, 0, sizeof(struct sigaction));
     memset(&act, 0, sizeof(struct sigaction));
  
     result = sigaction(DYNINST_BREAKPOINT_SIGNUM, NULL, &act);
     if (result == -1) {
        perror("DyninstRT library failed sigaction1");
        return;
     }
     act.sa_handler = uncaught_breakpoint;
  
     result = sigaction(DYNINST_BREAKPOINT_SIGNUM, &act, &oldact);
     if (result == -1) {
        perror("DyninstRT library failed sigaction2");
        return;
     }
  
     DYNINST_break_point_event = 1;
     failed_breakpoint = 0;
     kill(dyn_lwp_self(), DYNINST_BREAKPOINT_SIGNUM);
     if (failed_breakpoint) {
        DYNINST_break_point_event = 0;
        failed_breakpoint = 0;
     }
  
     result = sigaction(DYNINST_BREAKPOINT_SIGNUM, &oldact, NULL);
     if (result == -1) {
        perror("DyninstRT library failed sigaction3");
        return;
     }
  }
  
  void DYNINSTsafeBreakPoint()
  {
     if (DYNINSTstaticMode)
        return;
  
      DYNINST_break_point_event = 2; /* Not the same as above */
      //    while (DYNINST_break_point_event)
      kill(dyn_lwp_self(), SIGSTOP);
  }
  
  void mark_heaps_exec() {
  	/* Grab the page size, to align the heap pointer. */
  	long int pageSize = sysconf( _SC_PAGESIZE );
  	if( pageSize == 0 || pageSize == - 1 ) {
  		fprintf( stderr, "*** Failed to obtain page size, guessing 16K.\n" );
  		perror( "mark_heaps_exec" );
  		pageSize = 1024 * 16;
  		} /* end pageSize initialization */
  
  	/* Align the heap pointer. */
  	unsigned long int alignedHeapPointer = (unsigned long int) DYNINSTstaticHeap_16M_anyHeap_1;
  	alignedHeapPointer = (alignedHeapPointer) & ~(pageSize - 1);
  	unsigned long int adjustedSize = (unsigned long int) DYNINSTstaticHeap_16M_anyHeap_1 - alignedHeapPointer + sizeOfAnyHeap1;
  
  	/* Make the heap's page executable. */
  	int result = mprotect( (void *) alignedHeapPointer, (size_t) adjustedSize, PROT_READ | PROT_WRITE | PROT_EXEC );
  	if( result != 0 ) {
  		fprintf( stderr, "%s[%d]: Couldn't make DYNINSTstaticHeap_16M_anyHeap_1 executable!\n", __FILE__, __LINE__);
  		perror( "mark_heaps_exec" );
  		}
  	RTprintf( "*** Marked memory from 0x%lx to 0x%lx executable.\n", alignedHeapPointer, alignedHeapPointer + adjustedSize );
  
  	/* Mark _both_ heaps executable. */
  	alignedHeapPointer = (unsigned long int) DYNINSTstaticHeap_512K_lowmemHeap_1;
  	alignedHeapPointer = (alignedHeapPointer) & ~(pageSize - 1);
  	adjustedSize = (unsigned long int) DYNINSTstaticHeap_512K_lowmemHeap_1 - alignedHeapPointer + sizeOfLowMemHeap1;
  
  	/* Make the heap's page executable. */
  	result = mprotect( (void *) alignedHeapPointer, (size_t) adjustedSize, PROT_READ | PROT_WRITE | PROT_EXEC );
  	if( result != 0 ) {
  		fprintf( stderr, "%s[%d]: Couldn't make DYNINSTstaticHeap_512K_lowmemHeap_1 executable!\n", __FILE__, __LINE__ );
  		perror( "mark_heaps_exec" );
  		}
  	RTprintf( "*** Marked memory from 0x%lx to 0x%lx executable.\n", alignedHeapPointer, alignedHeapPointer + adjustedSize );
  	} /* end mark_heaps_exec() */
  
  /************************************************************************
   * void DYNINSTos_init(void)
   *
   * OS initialization function
  ************************************************************************/
  int DYNINST_sysEntry;
  
  #if !defined(DYNINST_RT_STATIC_LIB)
  /*
   * For now, removing dependence of static version of this library
   * on libdl.
   */
  typedef struct dlopen_args {
    const char *libname;
    int mode;
    void *result;
    void *caller;
  } dlopen_args_t;
  
  void *(*DYNINST_do_dlopen)(dlopen_args_t *) = NULL;
  
  static int get_dlopen_error() {
     char *err_str;
     err_str = dlerror();
     if (err_str) {
        strncpy(gLoadLibraryErrorString, err_str, (size_t) ERROR_STRING_LENGTH);
        return 1;
     }
     else {
        sprintf(gLoadLibraryErrorString,"unknown error with dlopen");
        return 0;
     }
     return 0;
  }
  
  int DYNINSTloadLibrary(char *libname)
  {
     void *res;
     gLoadLibraryErrorString[0]='\0';
     res = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL);
     if (res)
     {
        return 1;
     }
   
     get_dlopen_error();
  #if defined(arch_x86)
     /* dlopen on recent glibcs has a "security check" so that
        only registered modules can call it. Unfortunately, progs
        that don't include libdl break this check, so that we
        can only call _dl_open (the dlopen worker function) from
        within glibc. We do this by calling do_dlopen
        We fool this check by calling an addr written by the
        mutator */
     if (strstr(gLoadLibraryErrorString, "invalid caller") != NULL &&
         DYNINST_do_dlopen != NULL) {
        dlopen_args_t args;
        args.libname = libname;
        args.mode = RTLD_NOW | RTLD_GLOBAL;
        args.result = 0;
        args.caller = (void *)DYNINST_do_dlopen;
        // There's a do_dlopen function in glibc. However, it's _not_
        // exported; thus, getting the address is a bit of a pain. 
        
        (*DYNINST_do_dlopen)(&args);
        // Duplicate the above
        if (args.result != NULL)
        {
           return 1;
        }
        else
           get_dlopen_error();
     }
  #endif
     return 0;
  }
  #endif
  
  //Define this value so that we can compile on a system that doesn't have
  // gettid and still run on one that does.
  #if !defined(SYS_gettid)
  
  #if defined(arch_x86)
  #define SYS_gettid 224
  #elif defined(arch_x86_64)
  #define SYS_gettid 186
  #endif
  
  #endif
  
  int dyn_lwp_self()
  {
     static int gettid_not_valid = 0;
     int result;
  
     if (gettid_not_valid)
        return getpid();
  
     result = syscall((long int) SYS_gettid);
     if (result == -1 && errno == ENOSYS)
     {
        gettid_not_valid = 1;
        return getpid();
     }
     return result;  
  }
  
  int dyn_pid_self()
  {
     return getpid();
  }
  
  dyntid_t (*DYNINST_pthread_self)(void);
  
  dyntid_t dyn_pthread_self()
  {
     dyntid_t me;
     if (DYNINSTstaticMode) {
  #if defined(DYNINST_RT_STATIC_LIB)
         /* This special case is necessary because the static
          * version of libc doesn't define a version of pthread_self
          * unlike the shared version of the library.
          */
         if( !pthread_self ) {
             return (dyntid_t) DYNINST_SINGLETHREADED;
         }
  #endif
        return (dyntid_t) pthread_self();
     }
     if (!DYNINST_pthread_self) {
        return (dyntid_t) DYNINST_SINGLETHREADED;
     }
     me = (*DYNINST_pthread_self)();
     return (dyntid_t) me;
  }
  
  /* 
     We reserve index 0 for the initial thread. This value varies by
     platform but is always constant for that platform. Wrap that
     platform-ness here. 
  */
  int DYNINST_am_initial_thread( dyntid_t tid ) {
  	(void)tid; /* unused parameter */
  	if( dyn_lwp_self() == getpid() ) {
  		return 1;
     }
  	return 0;
  } /* end DYNINST_am_initial_thread() */
  
  #if defined(cap_mutatee_traps)
  
  #include <ucontext.h>
  
  // Register numbers experimentally verified
  
  #if defined(arch_x86)
    #define UC_PC(x) x->uc_mcontext.gregs[14]
  #elif defined(arch_x86_64)
    #if defined(MUTATEE_32)
      #define UC_PC(x) x->uc_mcontext.gregs[14]
    #else // 64-bit
      #define UC_PC(x) x->uc_mcontext.gregs[16]
    #endif // amd-64
  #elif defined(arch_power)
    #if defined(arch_64bit)
      #define UC_PC(x) x->uc_mcontext.regs->gpr[32]
    #else // 32-bit
      #define UC_PC(x) x->uc_mcontext.uc_regs->gregs[32]
    #endif // power
  #endif // UC_PC
  
  extern volatile unsigned long dyninstTrapTableUsed;
  extern volatile unsigned long dyninstTrapTableVersion;
  extern volatile trapMapping_t *dyninstTrapTable;
  extern volatile unsigned long dyninstTrapTableIsSorted;
  
  /**
   * This comment is now obsolete, left for historic purposes
   *
   * Called by the SIGTRAP handler, dyninstTrapHandler.  This function is 
   * closly intwined with dyninstTrapHandler, don't modify one without 
   * understanding the other.
   *
   * This function sets up the calling context that was passed to the
   * SIGTRAP handler so that control will be redirected to our instrumentation
   * when we do the setcontext call.
   * 
   * There are a couple things that make this more difficult than it should be:
   *   1. The OS provided calling context is similar to the GLIBC calling context,
   *      but not compatible.  We'll create a new GLIBC compatible context and
   *      copy the possibly stomped registers from the OS context into it.  The
   *      incompatiblities seem to deal with FP and other special purpose registers.
   *   2. setcontext doesn't restore the flags register.  Thus dyninstTrapHandler
   *      will save the flags register first thing and pass us its value in the
   *      flags parameter.  We'll then push the instrumentation entry and flags
   *      onto the context's stack.  Instead of transfering control straight to the
   *      instrumentation, we'll actually go back to dyninstTrapHandler, which will
   *      do a popf/ret to restore flags and go to instrumentation.  The 'retPoint'
   *      parameter is the address in dyninstTrapHandler the popf/ret can be found.
   **/
  
  void dyninstTrapHandler(int sig, siginfo_t *sg, ucontext_t *context)
  {
     void *orig_ip;
     void *trap_to;
     (void)sig; /* unused parameter */
     (void)sg; /* unused parameter */
  
     orig_ip = (void *) UC_PC(context);
     assert(orig_ip);
     // Find the new IP we're going to and substitute. Leave everything else untouched.
     if (DYNINSTstaticMode) {
        unsigned long zero = 0;
        unsigned long one = 1;
        struct trap_mapping_header *hdr = getStaticTrapMap((unsigned long) orig_ip);
        assert(hdr);
        volatile trapMapping_t *mapping = &(hdr->traps[0]);
        trap_to = dyninstTrapTranslate(orig_ip,
                                       (unsigned long *) &hdr->num_entries, 
                                       &zero, 
                                       &mapping,
                                       &one);
     }
     else {
        trap_to = dyninstTrapTranslate(orig_ip, 
                                       &dyninstTrapTableUsed,
                                       &dyninstTrapTableVersion,
                                       &dyninstTrapTable,
                                       &dyninstTrapTableIsSorted);
                                       
     }
     UC_PC(context) = (long) trap_to;
  }
  
  #if defined(cap_binary_rewriter)
  
  extern struct r_debug _r_debug;
  DLLEXPORT struct r_debug _r_debug __attribute__ ((weak));
  
  /* Verify that the r_debug variable is visible */
  void r_debugCheck() { assert(_r_debug.r_map); }
  
  #define NUM_LIBRARIES 512 //Important, max number of rewritten libraries
  
  #define WORD_SIZE (8 * sizeof(unsigned))
  #define NUM_LIBRARIES_BITMASK_SIZE (1 + NUM_LIBRARIES / WORD_SIZE)
  struct trap_mapping_header *all_headers[NUM_LIBRARIES];
  
  static unsigned all_headers_current[NUM_LIBRARIES_BITMASK_SIZE];
  static unsigned all_headers_last[NUM_LIBRARIES_BITMASK_SIZE];
  
  #if !defined(arch_x86_64) || defined(MUTATEE_32)
  typedef Elf32_Dyn ElfX_Dyn;
  #else
  typedef Elf64_Dyn ElfX_Dyn;
  #endif
  
  struct trap_mapping_header *getStaticTrapMap(unsigned long addr);
  
  static int parse_libs();
  static int parse_link_map(struct link_map *l);
  static void clear_unloaded_libs();
  
  static void set_bit(unsigned *bit_mask, int bit, char value);
  //static char get_bit(unsigned *bit_mask, int bit);
  static void clear_bitmask(unsigned *bit_mask);
  static unsigned get_next_free_bitmask(unsigned *bit_mask, int last_pos);
  static unsigned get_next_set_bitmask(unsigned *bit_mask, int last_pos);
  
  static tc_lock_t trap_mapping_lock;
  
  static struct trap_mapping_header *getStaticTrapMap(unsigned long addr)
  {
     struct trap_mapping_header *header;
     int i;
     
     tc_lock_lock(&trap_mapping_lock);
     parse_libs();
  
     i = -1;
     for (;;) {
        i = get_next_set_bitmask(all_headers_current, i);
        assert(i >= 0 && i <= NUM_LIBRARIES);
        if (i == NUM_LIBRARIES) {
           header = NULL;
           rtdebug_printf("%s[%d]:  getStaticTrapMap: returning NULL\n", __FILE__, __LINE__);
           goto done;
        }
        header = all_headers[i];
        if (addr >= header->low_entry && addr <= header->high_entry) {
           goto done;
        }
     }  
   done:
     tc_lock_unlock(&trap_mapping_lock);
     return header;
  }
  
  static int parse_libs()
  {
     struct link_map *l_current;
  
     l_current = _r_debug.r_map;
     if (!l_current) {
          rtdebug_printf("%s[%d]:  parse_libs: _r_debug.r_map was not set\n", __FILE__, __LINE__);
         return -1;
     }
  
     clear_bitmask(all_headers_current);
     while (l_current) {
        parse_link_map(l_current);
        l_current = l_current->l_next;
     }
     clear_unloaded_libs();
  
     return 0;
  }
  
  //parse_link_map return values
  #define PARSED 0
  #define NOT_REWRITTEN 1
  #define ALREADY_PARSED 2
  #define ERROR_INTERNAL -1
  #define ERROR_FULL -2
  static int parse_link_map(struct link_map *l) 
  {
     ElfX_Dyn *dynamic_ptr;
     struct trap_mapping_header *header;
     unsigned int i, new_pos;
  
     dynamic_ptr = (ElfX_Dyn *) l->l_ld;
     if (!dynamic_ptr)
        return -1;
  
     assert(sizeof(dynamic_ptr->d_un.d_ptr) == sizeof(void *));
     for (; dynamic_ptr->d_tag != DT_NULL && dynamic_ptr->d_tag != DT_DYNINST; dynamic_ptr++);
     if (dynamic_ptr->d_tag == DT_NULL) {
        return NOT_REWRITTEN;
     }
  
     header = (struct trap_mapping_header *) (dynamic_ptr->d_un.d_val + l->l_addr);
     
     if (header->signature != TRAP_HEADER_SIG)
        return ERROR_INTERNAL;
     if (header->pos != -1) {
        set_bit(all_headers_current, header->pos, 1);
        assert(all_headers[header->pos] == header);
        return ALREADY_PARSED;
     }
   
     for (i = 0; i < header->num_entries; i++)
     {
        header->traps[i].source = (void *) (((unsigned long) header->traps[i].source) + l->l_addr);
        header->traps[i].target = (void *) (((unsigned long) header->traps[i].target) + l->l_addr);
        if (!header->low_entry || header->low_entry > (unsigned long) header->traps[i].source)
           header->low_entry = (unsigned long) header->traps[i].source;
        if (!header->high_entry || header->high_entry < (unsigned long) header->traps[i].source)
           header->high_entry = (unsigned long) header->traps[i].source;
     }
  
     new_pos = get_next_free_bitmask(all_headers_last, -1);
     assert(new_pos < NUM_LIBRARIES);
     if (new_pos == NUM_LIBRARIES)
        return ERROR_FULL;
  
     header->pos = new_pos;
     all_headers[new_pos] = header;
     set_bit(all_headers_current, new_pos, 1);
     set_bit(all_headers_last, new_pos, 1);
  
     return PARSED;
  }
  
  static void clear_unloaded_libs()
  {
     unsigned i;
     for (i = 0; i<NUM_LIBRARIES_BITMASK_SIZE; i++)
     {
        all_headers_last[i] = all_headers_current[i];
     }
  }
  
  static void set_bit(unsigned *bit_mask, int bit, char value) {
     assert(bit < NUM_LIBRARIES);
     unsigned *word = bit_mask + bit / WORD_SIZE;
     unsigned shift = bit % WORD_SIZE;
     if (value) {
        *word |= (1 << shift);
     }
     else {
        *word &= ~(1 << shift);
     }
  }
  
  //Wasn't actually needed
  /*
  static char get_bit(unsigned *bit_mask, int bit) {
     assert(bit < NUM_LIBRARIES);
     unsigned *word = bit_mask + bit / WORD_SIZE;
     unsigned shift = bit % WORD_SIZE;
     return (*word & (1 << shift)) ? 1 : 0;
  }
  */
  
  static void clear_bitmask(unsigned *bit_mask) {
     unsigned i;
     for (i = 0; i < NUM_LIBRARIES_BITMASK_SIZE; i++) {
        bit_mask[i] = 0;
     }
  }
  
  static unsigned get_next_free_bitmask(unsigned *bit_mask, int last_pos) {
     unsigned i, j;
     j = last_pos+1;
     i = j / WORD_SIZE;
     for (; j < NUM_LIBRARIES; i++) {
        if (bit_mask[i] == (unsigned) -1) {
           j += WORD_SIZE;
           continue;
        }
        for (;;) {
           if (!((1 << (j % WORD_SIZE) & bit_mask[i]))) {
              return j;
           }
           j++;
           if (j % WORD_SIZE == 0) {
              break;
           }
        }
     }
     return NUM_LIBRARIES;
  }
  
  static unsigned get_next_set_bitmask(unsigned *bit_mask, int last_pos) {
     unsigned i, j;
     j = last_pos+1;
     i = j / WORD_SIZE;
     for (; j < NUM_LIBRARIES; i++) {
        if (bit_mask[i] == (unsigned) 0) {
           j += WORD_SIZE;
           continue;
        }
        for (;;) {
           if ((1 << (j % WORD_SIZE) & bit_mask[i])) {
              return j;
           }
           j++;
           if (j % WORD_SIZE == 0) {
              break;
           }
        }
     }
     return NUM_LIBRARIES;
  }
  
  #endif
  
  
  
  #endif /* cap_mutatee_traps */
  
  #if defined(cap_binary_rewriter) && !defined(DYNINST_RT_STATIC_LIB)
  /* For a static binary, all global constructors are combined in an undefined
   * order. Also, DYNINSTBaseInit must be run after all global constructors have
   * been run. Since the order of global constructors is undefined, DYNINSTBaseInit
   * cannot be run as a constructor in static binaries. Instead, it is run from a
   * special constructor handler that processes all the global constructors in
   * the binary. Leaving this code in would create a global constructor for the
   * function runDYNINSTBaseInit(). See DYNINSTglobal_ctors_handler.
   */ 
  extern void r_debugCheck();
  extern void DYNINSTBaseInit();
  void runDYNINSTBaseInit() __attribute__((constructor));
  void runDYNINSTBaseInit()
  {
      r_debugCheck();
     DYNINSTBaseInit();
  }
  #endif
  
  
  /*
  //Small program for finding the correct values to fill in pos_in_pthreadt
  // above
  #include <pthread.h>
  #include <stdio.h>
  #include <sys/syscall.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #define gettid() syscall(SYS_gettid)
  
  pthread_attr_t attr;
  
  void *foo(void *f) {
    pid_t pid, tid;
    unsigned stack_addr;
    unsigned best_stack = 0xffffffff;
    int best_stack_pos = 0;
    void *start_func;
    int *p;
    int i = 0;
    pid = getpid();
    tid = gettid();
    start_func = foo;
    //x86 only.  
    asm("movl %%ebp,%0" : "=r" (stack_addr));
    p = (int *) pthread_self();
    while (i < 1000)
    {
      if (*p == (unsigned) pid)
        printf("pid @ %d\n", i);
      if (*p == (unsigned) tid)
        printf("lwp @ %d\n", i);
      if (*p > stack_addr && *p < best_stack)
      {
        best_stack = *p;
        best_stack_pos = i;
      }
      if (*p == (unsigned) start_func)
        printf("func @ %d\n", i);
      i += sizeof(int);
      p++;
    }  
    printf("stack @ %d\n", best_stack_pos);
    return NULL;
  }
  
  int main(int argc, char *argv[])
  {
    pthread_t t;
    void *result;
    pthread_attr_init(&attr);
    pthread_create(&t, &attr, foo, NULL);
    pthread_join(t, &result);
    return 0;
  }
  */
  
  