/*
* linux/fs/exec.c
*
* (C) 1991 Linus Torvalds
*/

/*
* #!-checking implemented by tytso.
*/
/*
* #!ʼĳⲿtytso ʵֵġ
*/

/*
* Demand-loading implemented 01.12.91 - no need to read anything but
* the header into memory. The inode of the executable is put into
* "current->executable", and page faults do the actual loading. Clean.
*
* Once more I can proudly say that linux stood up to being changed: it
* was less than 2 hours work to get demand-loading completely implemented.
*/
/*
* ʱ1991.12.1 ʵֵ - ֻ轫ִļͷֶڴ
* ִļؽڴ档ִļi ڵ㱻ڵǰ̵Ŀִֶ
* ("current->executable")ҳ쳣ִļʵʼزԼ
*
* ҿһԺ˵linux ޸ģֻ˲2 СʱĹʱȫ
* ʵش
*/

#include <errno.h>		// ͷļϵͳиֳš(Linus minix )
#include <string.h>		// ַͷļҪһЩйַǶ뺯
#include <sys/stat.h>		// ļ״̬ͷļļļϵͳ״̬ṹstat{}ͳ
#include <a.out.h>		// a.out ͷļa.out ִļʽһЩꡣ

#include <linux/fs.h>		// ļϵͳͷļļṹfile,buffer_head,m_inode ȣ
#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣
#include <linux/mm.h>		// ڴͷļҳСһЩҳͷźԭ͡
#include <asm/segment.h>	// βͷļйضμĴǶʽຯ

extern int sys_exit (int exit_code);	// ˳ϵͳá
extern int sys_close (int fd);	// ļرϵͳá

/*
* MAX_ARG_PAGES defines the number of pages allocated for arguments
* and envelope for the new program. 32 should suffice, this gives
* a maximum env+arg of 128kB !
*/
/*
* MAX_ARG_PAGES ³ͻʹõڴҳ
* 32 ҳڴӦ㹻ˣʹûͲ(env+arg)ռܺϴﵽ128kB!
*/
#define MAX_ARG_PAGES 32

/*
* create_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
* addresses on the "stack", returning the new stack pointer value.
*/
/*
* create_tables()ûڴнͲַɴ
* ָǵĵַŵ"ջ"ϣȻ󷵻ջֵָ
*/
//// ûջдͲָ
// p - ݶΪĲͻϢƫָ룻argc - envc -
// أջָ롣
static unsigned long *
create_tables (char *p, int argc, int envc)
{
  unsigned long *argv, *envp;
  unsigned long *sp;

// ջָ4 ֽڣ1 ڣΪ߽Ѱַģsp Ϊ4 
  sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
// sp ƶճռõĿռûָenvp ָô
  sp -= envc + 1;
  envp = sp;
// sp ƶճвָռõĿռargv ָָô
// ָ1sp ֵָֽ
  sp -= argc + 1;
  argv = sp;
// ָenvp вָԼвѹջ
  put_fs_long ((unsigned long) envp, --sp);
  put_fs_long ((unsigned long) argv, --sp);
  put_fs_long ((unsigned long) argc, --sp);
// иָǰճӦطһNULL ָ롣
  while (argc-- > 0)
    {
      put_fs_long ((unsigned long) p, argv++);
      while (get_fs_byte (p++)) /* nothing */ ;	// p ָǰ4 ֽڡ
    }
  put_fs_long (0, argv);
// ָǰճӦطһNULL ָ롣
  while (envc-- > 0)
    {
      put_fs_long ((unsigned long) p, envp++);
      while (get_fs_byte (p++)) /* nothing */ ;
    }
  put_fs_long (0, envp);
  return sp;			// عĵǰ¶ջָ롣
}

/*
* count() counts the number of arguments/envelopes
*/
/*
* count()в/ĸ
*/
//// 
// argv - ָ飬һָNULL
// أ
static int
count (char **argv)
{
  int i = 0;
  char **tmp;

  if (tmp = argv)
    while (get_fs_long ((unsigned long *) (tmp++)))
      i++;

  return i;
}

/*
* 'copy_string()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory.
*
* Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
* whether the string and the string array are from user or kernel segments:
*
* from_kmem argv * argv **
* 0 user space user space
* 1 kernel space user space
* 2 kernel space kernel space
*
* We do this by playing games with the fs segment register. Since it
* it is expensive to load a segment register, we try to avoid calling
* set_fs() unless we absolutely have to.
*/
/*
* 'copy_string()'ûڴռ俽ͻַں˿ҳڴС
* ЩѾֱӷŵûڴеĸʽ
*
* TYT(Tytso)1991.12.24 ޸ģfrom_kmem òַָ
* ַûλں˶Ρ
*
* from_kmem argv * argv **
* 0 ûռ ûռ
* 1 ں˿ռ ûռ
* 2 ں˿ռ ں˿ռ
*
* ͨfs μĴġڼһμĴ̫
* Ǿset_fs()ʵڱҪ
*/
//// ָĲַͻռ䡣
// argc - ӵĲargv - ָ飻page - ͻռҳָ顣
// p -ڲռеƫָ룬ʼָѸƴͷfrom_kmem - ַԴ־
// do_execve()Уp ʼΪָ(128kB)ռһִַ
// Զջʽиƴŵģp ָʼַָͷ
// أͻռ䵱ǰͷָ롣
static unsigned long
copy_strings (int argc, char **argv, unsigned long *page,
	      unsigned long p, int from_kmem)
{
  char *tmp, *pag;
  int len, offset = 0;
  unsigned long old_fs, new_fs;

  if (!p)
    return 0;			/* bullet-proofing *//* ƫָ֤ */
// ȡds Ĵֵnew_fsԭfs Ĵֵold_fs
  new_fs = get_ds ();
  old_fs = get_fs ();
// ַַں˿ռ䣬fs μĴָںݶΣds
  if (from_kmem == 2)
    set_fs (new_fs);
// ѭһʼƣƵָƫƵַ
  while (argc-- > 0)
    {
// ַûռַں˿ռ䣬fs μĴָںݶΣds
      if (from_kmem == 1)
	set_fs (new_fs);
// һʼȡfs һָ뵽tmpΪգ
      if (!(tmp = (char *) get_fs_long (((unsigned long *) argv) + argc)))
	panic ("argc is wrong");
// ַûռַں˿ռ䣬ָfs μĴԭֵ
      if (from_kmem == 1)
	set_fs (old_fs);
// òַlenʹtmp ָòַĩˡ
      len = 0;			/* remember zero-padding */
      do
	{			/* ֪NULL ֽڽβ */
	  len++;
	}
      while (get_fs_byte (tmp++));
// ַȳʱͻռлʣĿгȣָfs μĴ0
      if (p - len < 0)
	{			/* this shouldn't happen - 128kB */
	  set_fs (old_fs);	/* ᷢ-Ϊ128kB Ŀռ */
	  return 0;
	}
// fs еǰָĲַǴӸַβʼơ
      while (len)
	{
	  --p;
	  --tmp;
	  --len;
// տʼִʱƫƱoffset ʼΪ0offset-1<0˵״θַ
// p ָҳڵƫֵҳ档
	  if (--offset < 0)
	    {
	      offset = p % PAGE_SIZE;
// ַַں˿ռ䣬ָfs μĴԭֵ
	      if (from_kmem == 2)
		set_fs (old_fs);
// ǰƫֵp ڵĴռҳָpage[p/PAGE_SIZE]==0ʾӦҳ滹ڣ
// µڴҳ棬ҳָָ飬Ҳʹpag ָҳ棬벻
// ҳ򷵻0
	      if (!(pag = (char *) page[p / PAGE_SIZE]) &&
		  !(pag = (char *) page[p / PAGE_SIZE] =
		    (unsigned long *) get_free_page ()))
		return 0;
// ַַں˿ռ䣬fs μĴָںݶΣds
	      if (from_kmem == 2)
		set_fs (new_fs);

	    }
// fs иƲַһֽڵpag+offset 
	  *(pag + offset) = get_fs_byte (tmp);
	}
    }
// ַַں˿ռ䣬ָfs μĴԭֵ
  if (from_kmem == 2)
    set_fs (old_fs);
// 󣬷زͻռѸƲϢͷƫֵ
  return p;
}

//// ޸ľֲеַͶ޳ͻռҳݶĩˡ
// text_size - ִļͷa_text ֶθĴγֵ
// page - ͻռҳָ顣
// أݶ޳ֵ(64MB)
static unsigned long
change_ldt (unsigned long text_size, unsigned long *page)
{
  unsigned long code_limit, data_limit, code_base, data_base;
  int i;

// ִļͷa_text ֵҳ泤Ϊ߽Ĵ޳ݶγΪ64MB
  code_limit = text_size + PAGE_SIZE - 1;
  code_limit &= 0xFFFFF000;
  data_limit = 0x4000000;
// ȡǰоֲдλַλַݶλַͬ
  code_base = get_base (current->ldt[1]);
  data_base = code_base;
// þֲдκݶĻַͶ޳
  set_base (current->ldt[1], code_base);
  set_limit (current->ldt[1], code_limit);
  set_base (current->ldt[2], data_base);
  set_limit (current->ldt[2], data_limit);
/* make sure fs points to the NEW data segment */
/* Ҫȷfs μĴָµݶ */
// fs μĴзֲݶѡ(0x17)
  __asm__ ("pushl $0x17\n\tpop %%fs"::);
// ͻռѴݵҳ棨MAX_ARG_PAGES ҳ128kBŵݶԵַ
// ĩˡǵúput_page()вģmm/memory.c, 197
  data_base += data_limit;
  for (i = MAX_ARG_PAGES - 1; i >= 0; i--)
    {
      data_base -= PAGE_SIZE;
      if (page[i])		// ҳڣ
	put_page (page[i], data_base);	// ͷøҳ档
    }
  return data_limit;		// 󷵻ݶ޳(64MB)
}

/*
* 'do_execve()' executes a new program.
*/
/*
* 'do_execve()'ִһ³
*/
//// execve()ϵͳжϵúزִӽ̣򣩡
// úϵͳжϵ(int 0x80)ܺ__NR_execve õĺ
// eip - ָջеϵͳжϵĳָeip μkernel/system_call.s 
// ʼֵ˵tmp - ϵͳжϵñʱķصַã
// filename - ִгļargv - вָ飻envp - ָ顣
// أóɹ򲻷أóţ-1
int
do_execve (unsigned long *eip, long tmp, char *filename,
	   char **argv, char **envp)
{
  struct m_inode *inode;	// ڴI ڵָṹ
  struct buffer_head *bh;	// ٻͷָ롣
  struct exec ex;		// ִļͷݽṹ
  unsigned long page[MAX_ARG_PAGES];	// ͻַռҳָ顣
  int i, argc, envc;
  int e_uid, e_gid;		// Чûid Чid
  int retval;			// ֵ
  int sh_bang = 0;		// ǷҪִнű롣
// ͻַռеƫָ룬ʼΪָÿռһִ
  unsigned long p = PAGE_SIZE * MAX_ARG_PAGES - 4;

// eip[1]ԭμĴcsеѡں˶ѡҲں˲ܵñ
  if ((0xffff & eip[1]) != 0x000f)
    panic ("execve called from supervisor mode");
// ʼͻռҳָ飨
  for (i = 0; i < MAX_ARG_PAGES; i++)	/* clear page-table */
    page[i] = 0;
// ȡִļĶӦi ڵš
  if (!(inode = namei (filename)))	/* get executables inode */
    return -ENOENT;
// ͻ
  argc = count (argv);
  envc = count (envp);

// ִļǳļǳļó룬תexec_error2(347 )
restart_interp:
  if (!S_ISREG (inode->i_mode))
    {				/* must be regular file */
      retval = -EACCES;
      goto exec_error2;
    }
// 鱻ִļִȨޡ(Ӧi ڵuid gid)ǷȨִ
  i = inode->i_mode;
  e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
  e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
  if (current->euid == inode->i_uid)
    i >>= 6;
  else if (current->egid == inode->i_gid)
    i >>= 3;
  if (!(i & 1) && !((inode->i_mode & 0111) && suser ()))
    {
      retval = -ENOEXEC;
      goto exec_error2;
    }
// ȡִļĵһݵٻó룬תexec_error2 ȥ
  if (!(bh = bread (inode->i_dev, inode->i_zone[0])))
    {
      retval = -EACCES;
      goto exec_error2;
    }
// ִļͷṹݽдex ִָͷֵݽṹ
  ex = *((struct exec *) bh->b_data);	/* read exec-header *//* ȡִͷ */
// ִļʼֽΪ'#!'sh_bang ־ûλűļִС
  if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang))
    {
/*
* This section does the #! interpretation.
* Sorta complicated, but hopefully it will work. -TYT
*/
/*
* ⲿִ'#!'ĽͣЩӣϣܹ-TYT
*/

      char buf[1023], *cp, *interp, *i_name, *i_arg;
      unsigned long old_fs;

// ִгͷһַ'#!'ַbuf Укнű
      strncpy (buf, bh->b_data + 2, 1022);
// ͷŸٻ͸ִļi ڵ㡣
      brelse (bh);
      iput (inode);
// ȡһݣɾʼĿոƱ
      buf[1022] = '\0';
      if (cp = strchr (buf, '\n'))
	{
	  *cp = '\0';
	  for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
	}
// ûݣó룬תexec_error1 
      if (!cp || *cp == '\0')
	{
	  retval = -ENOEXEC;	/* No interpreter name found */
	  goto exec_error1;
	}
// ͵õ˿ͷǽűִгƵһݡ
      interp = i_name = cp;
// СȡһַӦǽűͳiname ָơ
      i_arg = 0;
      for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
	{
	  if (*cp == '/')
	    i_name = cp + 1;
	}
// ļַӦǲi_arg ָô
      if (*cp)
	{
	  *cp++ = '\0';
	  i_arg = cp;
	}
/*
* OK, we've parsed out the interpreter name and
* (optional) argument.
*/
/*
* OKѾͳļԼ(ѡ)
*/
// sh_bang ־ûãָĻͲͻռС
      if (sh_bang++ == 0)
	{
	  p = copy_strings (envc, envp, page, p, 0);
	  p = copy_strings (--argc, argv + 1, page, p, 0);
	}
/*
* Splice in (1) the interpreter's name for argv[0]
* (2) (optional) argument to interpreter
* (3) filename of shell script
*
* This is done in reverse order, because of how the
* user environment and arguments are stored.
*/
/*
* ƴ (1) argv[0]зŽͳ
* (2) (ѡ)ͳĲ
* (3) ű
*
* дģûͲĴŷʽɵġ
*/
// ƽűļͻռС
      p = copy_strings (1, &filename, page, p, 1);
// ƽͳĲͻռС
      argc++;
      if (i_arg)
	{
	  p = copy_strings (1, &i_arg, page, p, 2);
	  argc++;
	}
// ƽͳļͻռСó룬תexec_error1
      p = copy_strings (1, &i_name, page, p, 2);
      argc++;
      if (!p)
	{
	  retval = -ENOMEM;
	  goto exec_error1;
	}
/*
* OK, now restart the process with the interpreter's inode.
*/
/*
* OKʹýͳi ڵ̡
*/
// ԭfs μĴԭָûݶΣָںݶΡ
      old_fs = get_fs ();
      set_fs (get_ds ());
// ȡͳi ڵ㣬תrestart_interp ´
      if (!(inode = namei (interp)))
	{			/* get executables inode */
	  set_fs (old_fs);
	  retval = -ENOENT;
	  goto exec_error1;
	}
      set_fs (old_fs);
      goto restart_interp;
    }
// ͷŸû
  brelse (bh);
// ִͷϢд
// ִгִļҳִļ(ZMAGIC)ߴضλ
// a_trsize 0ضλϢȲ0ߴ+ݶ+Ѷγȳ50MB
// i ڵĸִļСڴ+ݶ+ű+ִͷֳȵܺ͡
  if (N_MAGIC (ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
      ex.a_text + ex.a_data + ex.a_bss > 0x3000000 ||
      inode->i_size < ex.a_text + ex.a_data + ex.a_syms + N_TXTOFF (ex))
    {
      retval = -ENOEXEC;
      goto exec_error2;
    }
// ִļִͷֳȲһڴС1024 ֽڣҲִСתexec_error2
  if (N_TXTOFF (ex) != BLOCK_SIZE)
    {
      printk ("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
      retval = -ENOEXEC;
      goto exec_error2;
    }
// sh_bang ־ûãָĻַͲͻռС
// sh_bang ־Ѿãǽнű򣬴ʱҳѾƣٸơ
  if (!sh_bang)
    {
      p = copy_strings (envc, envp, page, p, 0);
      p = copy_strings (argc, argv, page, p, 0);
// p=0ʾռҳѾռɲˡת
      if (!p)
	{
	  retval = -ENOMEM;
	  goto exec_error2;
	}
    }
/* OK, This is the point of no return */
/* OK濪ʼûзصĵط */
// ԭҲһִгͷi ڵ㣬ýexecutable ֶָ³i ڵ㡣
  if (current->executable)
    iput (current->executable);
  current->executable = inode;
// 帴λźŴSIG_IGN ܸλ322 323 ֮һ
// if 䣺if (current->sa[I].sa_handler != SIG_IGN)Դеһbug
  for (i = 0; i < 32; i++)
    current->sigaction[i].sa_handler = NULL;
// ִʱر(close_on_exec)ļλͼ־رָĴļλñ־
  for (i = 0; i < NR_OPEN; i++)
    if ((current->close_on_exec >> i) & 1)
      sys_close (i);
  current->close_on_exec = 0;
// ָĻַ޳ͷԭκݶӦڴҳָڴ鼰ҳ
  free_page_tables (get_base (current->ldt[1]), get_limit (0x0f));
  free_page_tables (get_base (current->ldt[2]), get_limit (0x17));
// ϴʹЭָǵǰ̣ÿգλʹЭı־
  if (last_task_used_math == current)
    last_task_used_math = NULL;
  current->used_math = 0;
// a_text ޸ľֲַͶ޳ͻռҳݶĩˡ
// ִ֮p ʱݶʼΪԭƫֵָͻռݿʼ
// ҲתΪջָ롣
  p += change_ldt (ex.a_text, page) - MAX_ARG_PAGES * PAGE_SIZE;
// create_tables()ûջдͲָظöջָ롣
  p = (unsigned long) create_tables ((char *) p, argc, envc);
// ޸ĵǰ̸ֶΪִгϢ̴βֵֶend_code = a_text
// βֶend_data = a_data + a_text̶ѽβֶbrk = a_text + a_data + a_bss
  current->brk = ex.a_bss +
    (current->end_data = ex.a_data + (current->end_code = ex.a_text));
// ý̶ջʼֶΪջָڵҳ棬ý̵ûid id
  current->start_stack = p & 0xfffff000;
  current->euid = e_uid;
  current->egid = e_gid;
// ʼһҳbss ݣȫΪ㡣
  i = ex.a_text + ex.a_data;
  while (i & 0xfff)
    put_fs_byte (0, (char *) (i++));
// ԭϵͳжϵĳڶջϵĴָ滻Ϊִָгڵ㣬ջָ滻
// ΪִгĶջָ롣ָЩջݲʹCPU ȥִµִг˲
// صԭϵͳжϵĳȥˡ
  eip[0] = ex.a_entry;		/* eip, magic happens :-) *//* eipħ */
  eip[3] = p;			/* stack pointer *//* espջָ */
  return 0;
exec_error2:
  iput (inode);
exec_error1:
  for (i = 0; i < MAX_ARG_PAGES; i++)
    free_page (page[i]);
  return (retval);
}
