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

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU 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
General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program; see the file COPYING.  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

#include "machine.h"
#include "message.h"

/* static */ int chkr_is_init;

/* for show_frames. */
__ptr_t *chkr_frames_ip;
__ptr_t *chkr_frames_pointer;
int chkr_frames_to_forget;

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

extern __ptr_t chkr_address (__ptr_t ptr);
/*
 * 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)MIN_ADDRESS;
	return(retval);
}

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

 /* 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 *)STACK_LIMIT; ptr++)
 {
#if 0	/* FIXME */
   if(*ptr >= min && *ptr < max) /* does not point on heap */
#endif   
     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;
}
 
void
chkr_show_frames()
{
 __ptr_t *frame;
 int i;
 int forget = chkr_frames_to_forget;
  
 chkr_printf(M_STACK_FRAMES_ARE);
 chkr_load_symtab();
 
 /* 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...
  */
 if (chkr_frames_pointer == (__ptr_t*)0)
 {
   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 ))
       goto end;
     else
      frame = (__ptr_t*)*frame;
   }
 }
 else
   frame = chkr_frames_pointer;
   
 if (chkr_frames_ip)
   chkr_show_addr((__ptr_t*)&chkr_frames_ip);
 
 while(1)
   {
      chkr_show_addr((__ptr_t*)&frame[1]);
      if (!is_ok_frame( frame ))
        goto end;
      frame = (__ptr_t*)*frame;
   }
 
  end:
 chkr_frames_ip = (__ptr_t*)0;
 chkr_frames_pointer = (__ptr_t*)0;
 chkr_unload_symtab();
 return;
}

void __chkr_init_chkr(int argc, char *argv[], char *argp[]);

/* 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;
 
 /* If checker is already initialized, return now */
 if (chkr_is_init)
   return;
   
 /* search the frame of 'main' */
 frame = (__ptr_t*)&frame + 1; /* get the stack pointer from &frame */ 
			       /* 1 is 4 bytes */
 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)
   __chkr_init_chkr((int)frame[2], (char**)frame[3], (char**)frame[4]);
 else
   chkr_header(M_CANT_FIND_MAIN);

 return;
}

#endif /* CHKR_SAVESTACK */
