/* Generic implementation of system-dependent functions.
   Copyright 1993, 1994, 1995 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., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.

The author may be reached by US/French mail:
		Tristan Gingold 
		8 rue Parmentier
		F-91120 PALAISEAU
		FRANCE
*/

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

/* True if Checker was already initialized. */
int chkr_is_init;

/* The stack base */
PTR stack_base;

/* For show_frames. */
PTR chkr_frames_ip;
PTR chkr_frames_pointer;
int chkr_frames_to_forget;
PTR min_address = (PTR)MIN_ADDRESS;

#ifdef CHKR_GARBAGE

extern void chkr_address (PTR ptr, int sure);

/* Check to see if the stack frame is still cool. */
static int
is_ok_frame(PTR *t)
{
  int retval;
#ifdef STACK_GROWS_DOWNWARD
  retval = (t >= (PTR *)&t) && (t <= (PTR *)stack_base);
#else
  retval = (t >= (PTR *)stack_base) && (t <= (PTR *)&t);
#endif
  return retval;
}

/* Search pointer inside the stack segment.
 * Check between &dummy and the top of the stack */
void
search_stack(void)
{
 PTR dummy;	/* OK, we could use ptr or min, but dummy is clearer */
 PTR *ptr;

#ifdef STACK_GROWS_DOWNWARD
 for (ptr = &dummy; ptr < (PTR *)stack_base; ptr++)
#else
 for (ptr = (PTR *)stack_base; ptr < &dummy; ptr++)
#endif
   {
     if (*ptr >= low_addr_heap && *ptr < high_addr_heap) /* does not point on heap */
       chkr_address(*ptr, 1);
   }    
}
#endif /* CHKR_SAVESTACK */

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

 /* Really configuration dependant. */
 frame = __builtin_current_frame_pointer();
 
 /* we must 'forget' forget frames */
 for(i = 0; i < forget; i++)
   {
     if (!is_ok_frame(frame))
       return;
     else
       frame = __builtin_previous_frame_pointer(frame);
   }
 
 /* save num frames */
 for(i = num-1; i > 0; i--)
   {
     if (!is_ok_frame(frame))
       {
         *ptr = (PTR)0;
         return;
       }
     *ptr++ = __builtin_return_address_from_frame_pointer(frame);
     frame = __builtin_previous_frame_pointer(frame);
   }
 *ptr = (PTR)0;
}
 
void
chkr_show_frames(void)
{
 PTR *frame;
 int i;
 int forget = chkr_frames_to_forget;
  
 chkr_printf(M_STACK_FRAMES_ARE);
 chkr_load_symtab();
 
 /* Really configuration dependant. */
 if (chkr_frames_pointer == (PTR)0)
   {
     frame = __builtin_current_frame_pointer();
     
     /* we must 'forget' forget frames */
     for(i = 0; i < forget; i++)
       {
         if (!is_ok_frame( frame ))
           goto end;
         else
           frame = __builtin_previous_frame_pointer(frame);
       }
   }
 else
   frame = chkr_frames_pointer;
   
 if (chkr_frames_ip)
   chkr_show_addr(chkr_frames_ip);
 
 while(1)
   {
      if (!is_ok_frame(frame))
        goto end;
      chkr_show_addr(__builtin_return_address_from_frame_pointer(frame));
      frame = __builtin_previous_frame_pointer(frame)
   }
 
  end:
 chkr_frames_ip = (PTR)0;
 chkr_frames_pointer = (PTR)0;
 chkr_unload_symtab();
 return;
}

void
chkr_save_frames(PTR *ptr, int forget, int num)
{
 PTR *frame;
 int i;
 int n;
 
 forget += chkr_frames_to_forget;
  
 /* Really configuration dependant. */
 n = 0;
 num--;
 if (num == 0)
   return;
   
 if (chkr_frames_pointer == (PTR)0)
   {
     frame = __builtin_current_frame_pointer();
     
     /* we must 'forget' forget frames */
     for(i = 0; i < forget; i++)
       {
         if (!is_ok_frame(frame))
           return;
         else
           frame = __builtin_previous_frame_pointer(frame);
       }
   }
 else
   frame = chkr_frames_pointer;
   
 if (chkr_frames_ip)
   ptr[n++] = chkr_frames_ip;
 
 if (n == num)
   {
     ptr[n] = (PTR)0;
     return;
   }
    
 while(1)
   {
      if (n == num || !is_ok_frame(frame))
        {
          ptr[n] = (PTR)0;
          return;
        }
      ptr[n++] = __builtin_return_address_from_frame_pointer(frame);
      frame = __builtin_previous_frame_pointer(frame);
   }
 
 return;
}

/* 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(void)
{
 /* If checker is already initialized, return now */
 if (chkr_is_init)
   return;
}

/* This is called by parse-args.c(___chkr_init_chkr) */
void
chkr_init_machine(int *argc, char *argv[], char *envp[])
{
#ifndef STACK_BASE
 /* STACK_BASE can be not defined since it can be floating.  However, the
  * stack direction is not floating. */
#ifdef STACK_GROWS_DOWNWARD
#define BASE(a,b) ((a) > (b) ? (a) : (b))
#else
#define BASE(a,b) ((a) < (b) ? (a) : (b))
#endif
  stack_base = (char*)argc;
  for (i = 0; i < argc; i++)
    {
      p = argv[i];
      stack_base = BASE(stack_base, p);
      p = argv[i] + strlen(argv[i]) + 1;
      stack_base = BASE(stack_base, p);
    }
  for (i = 0; envp[i]; i++)
    {
      p = envp[i];
      stack_base = BASE(stack_base, p);
      p = envp[i] + strlen(envp[i]) + 1;
      stack_base = BASE(stack_base, p);
    }
  stack_base = (char*)((int)stack_base + ps - 1) & ~(ps-1);
#endif

 /* Must be set now */
 low_addr_heap = (PTR)(objects->bss_end);
 min_address = (PTR)(objects->text_org + MIN_ADDRESS);
}

#endif /* CHKR_SAVESTACK */
