/* startup for gccchecker.
   Copyright 1995 Tristan Gingold
		  Written June 1995 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 <elf.h>

/* This is the machine dependant first entry point of Checker.  */
/* If STARTUP_IS_MAIN is defined, then crt.o calls main, which is the
   following function (because of asm).  This function initialize
   Checker and then call user main (which has a prefix).
   If STARTUP_IS_MAIN is not defined, then the following function should
   be call before main.  This can be done with a call inserted in the
   .init section.  */

#ifdef STARTUP_IS_MAIN
int startup_call_main (int argc, char *argv[], char *envp[]);
int startup (int argc, char *argv[], char *envp[]) __asm__ ("main");
#endif

typedef struct
{
  int	a_type;
  union{
    long a_val;
    void *p_ptr;
    void (*a_fcn)(void);
  } a_un;
} auxv_t;

#ifndef AT_NULL
#define AT_NULL		0
#define AT_IGNORE	1
#define AT_EXECFD	2
#define AT_PHDR		3
#define AT_PHENT	4
#define AT_PHNUM	5
#define AT_PAGESZ	6
#define AT_BASE		7
#define AT_FLAGS       	8
#define AT_ENTRY	9
#endif

extern char **environ;
extern char **_environ;

#define SEND_STDERR(mes) chkr_write(2,mes,strlen(mes))

#ifdef STARTUP_IS_MAIN
int
startup (int argc, char *argv[], char *envp[])
#else
void
startup (int *args)
#endif
{
  Elf32_Phdr *phdr;
  int num;
  
  /* Search AT_PHDR. */
  {
    int *tmp;
    auxv_t *auxv;

#ifndef LINUX_GLIBC1
    for (; *((int **)args) != (int *)_environ; args++)
      ;
    args -= 2;
#endif

#ifdef STARTUP_IS_MAIN
    tmp = (int *)argv;
#else
    /* Args points on argc.  */
    tmp = (int *)(args[1]);
#endif

    /* Skip over the argv pointers. */
    while (*tmp)
      tmp++;
    /* Skip the null */
    tmp++;
    /* Skip over the envp pointers. */
    while (*tmp)
      tmp++;
    /* Skip the null. */
    tmp++;
    phdr = (Elf32_Phdr*) 0;
    num = 0;
    for (auxv = (auxv_t*)tmp; auxv->a_type; auxv++)
      switch (auxv->a_type)
	{
	case AT_PHDR:
          phdr = (Elf32_Phdr*)auxv->a_un.a_val;
	  break;
	case AT_PHNUM:
          num = auxv->a_un.a_val;
	  break;
	}
#if 0
    chkr_printf ("auxv: 0x%08x\n", auxv);
    chkr_printf ("phdr: 0x%08x\n", phdr);
#endif
  }
  
#if 0
  chkr_printf("Call initialize\n");
#endif

#ifdef STARTUP_IS_MAIN
  /* Initialize the environ */
  environ = envp;

  /* Initialize Checker.  */
  ___chkr_init_chkr (1, num, (char**) phdr,
		     argc, argv, envp);

  return startup_call_main (argc, argv, envp);
#else
  /* Initialize the environ */
  environ = (char **)(args[2]);

  /* Initialize Checker.  */
  ___chkr_init_chkr (1, num, (char**) phdr,
		     args[0], (char **)args[1], (char **)args[2]);

  chkr_set_right (&args[0], sizeof (int), CHKR_RW);
  chkr_set_right (&args[1], sizeof (char *), CHKR_RW);
  chkr_set_right (&args[2], sizeof (char *), CHKR_RW);
#endif
}





