/* Memory function for Linux systems.
   Copyright 1993 Tristan Gingold
		  Written August 1993 Tristan Gingold

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE */

#ifndef	_MALLOC_INTERNAL
#define _MALLOC_INTERNAL
#include "malloc.h"
#endif

#ifdef CHKR_GARBAGE
#ifndef linux
#error Your OS is not Linux. See README file
#endif

/*
 * Check to see if the stack frame is still cool.  We use the fact
 * that linux's stack is always in the same place to know when we've
 * strayed off the stack.  At this point continuing is rather useless,
 * so we punt.  This avoids a core dump and is about as unportable as
 * it gets, but the name of this module is sysdep, no?
 */
#define LINUX_STACK_MASK 0xbff00000
static int is_ok_frame(__ptr_t *t)
{
	int retval;
	retval = ((unsigned int) t & LINUX_STACK_MASK) == LINUX_STACK_MASK &&
			t[1] > (__ptr_t)0x30;
	return(retval);
}

/* search pointer inside the stack segment 
 * check between &dummy and the top of the stack */
void search_stack()
{
 __ptr_t dummy;	/* OK, we could use ptr or min, but dummy is clearer */
 __ptr_t *ptr;
 __ptr_t min=_heapbase;
 __ptr_t max=ADDRESS(_heaplimit);

 /* This code is not really portable.
   We can know how the stack is growing
   We know where the stack pointer is
   But I don't know how to know the top of the stack.
   This top can change only while a process is created. (exec, fork, clone)
   So, just after main, we can insert code to know the top.
   An other way is to make a core-file ... 
   Or to try and waiting for SIGSEGV */
 for(ptr=&dummy; ptr<(__ptr_t *)0xc0000000; ptr++)
 {
   if(*ptr == _heapbase) /* avoid it */
     continue;
   if(*ptr >= min && *ptr < max) /* does not point on heap */
     chkr_address(*ptr);
 }    
}
#endif /* CHKR_SAVESTACK */

#ifdef CHKR_SAVESTACK
/* save the stack ( in fact, only the return addresses)
 * used by malloc
 */
void chkr_save_stack(__ptr_t *ptr, int forget, int num)
{
 __ptr_t *frame;
 int i;
 
 *ptr = (__ptr_t)0;
 
 if (num == 0)
   return;

 /* Really configuration dependant. 
    We must know:
    1) How to access to the frame pointer from an arg
    2) How does the stack grows (easy)
    3) What is the first frame (difficult)
    For linux, I check the return address of the frame. If this address
     is inside (!?) crt0.o, this is the last frame.
    Of course, I could check the next frame pointer, but it is harzardous
      and I must known the top of the stack...
  */
 frame = (__ptr_t*)&frame + 1; /* get the stack pointer from &frame */ 
			       /* 1 is 4 bytes */ 
 /* we must 'forget' forget frames */
 for(i=0; i<forget; i++)
 {
    if (!is_ok_frame( frame ))
     return;
    else
     frame=(__ptr_t*)*frame;
 }
 
 /* save num frames */
 for(i=num-1; i>0; i--)
 {
    *ptr++=(__ptr_t)frame[1];
    if (!is_ok_frame( frame ))
    {
      *ptr= (__ptr_t)0;
      return;
    }
    frame=(__ptr_t*)*frame;
 }
 *ptr= (__ptr_t)0;
}
 
/* fonction called by malloc.c:initialize() 
 * It is called once time, and can use malloc/free (e.g. getcwd)
 * search the full name. 
 */
void chkr_initialize()
{
 __ptr_t *frame;
 char **argv;
 
 chkr_errno = 0;
 /* some trivial checks. ??_red_zone must be a multiple of 4 */
 if ( be_red_zone % sizeof(void*) || af_red_zone % sizeof(void*))
 {
   chkr_errno = 6;
   chkr_perror();
 }
 frame = (__ptr_t*)&frame + 1; /* get the stack pointer from &frame */ 
			       /* 1 is 4 bytes */
 /* search the frame of 'main' */			       
 while (is_ok_frame( frame ))
   frame=(__ptr_t*)*frame;
 
 /* frame[1] is the return address
    frame[2] is argc
    frame[3] is argv
    frame[4] is argp
 */
 if (frame) {
   argv = (char**)frame[3];
   chkr_prog_path = chkr_find_executable(argv[0]);
   if (chkr_prog_path == (char*)0)
     (*__chkr_trap)(1,"Can't find program name.");
 }
 else
   (*__chkr_trap)(1,"Can't find main() frame, you lose");
  
 return;
}

static void f_show_frames(int status)
{
 __ptr_t *frame;
 int i;
 int forget=5;
  
 /* Really configuration dependant. 
    We must know:
    1) How to access to the frame pointer from an arg
    2) How does the stack grows (easy)
    3) What is the first frame (difficult)
    For linux, I check the return address of the frame. If this address
     is inside (!?) crt0.o, this is the last frame.
    Of course, I could check the next frame pointer, but it is harzardous
      and I must known the top of the stack...
  */
 frame = (__ptr_t*)&frame + 1; /* get the stack pointer from &frame */ 
			       /* 1 is 4 bytes */ 
 /* we must 'forget' forget frames */
 for(i=0; i<forget; i++)
 {
    if (!is_ok_frame( frame ))
     return;
    else
     frame=(__ptr_t*)*frame;
 }
 
 while(1)
 {
    chkr_show_addr((__ptr_t*)&frame[1]);
    if (!is_ok_frame( frame ))
      return;
    frame=(__ptr_t*)*frame;
 }
}

void chkr_show_frames()
{
 (*__chkr_trap)(1,"Stack frames are:");
 chkr_use_symtab(f_show_frames);
}
#endif /* CHKR_SAVESTACK */
