#include <linux/mm.h>
      #include <linux/smp_lock.h>
      #include <linux/module.h>
      
      static asmlinkage void no_lcall7(int segment, struct pt_regs * regs);
      
      
      static unsigned long ident_map[32] = {
      	0,	1,	2,	3,	4,	5,	6,	7,
      	8,	9,	10,	11,	12,	13,	14,	15,
      	16,	17,	18,	19,	20,	21,	22,	23,
      	24,	25,	26,	27,	28,	29,	30,	31
      };
      
      struct exec_domain default_exec_domain = {
      	"Linux",	/* name */
      	no_lcall7,	/* lcall7 causes a seg fault. */
      	0, 0xff,	/* All personalities. */
      	ident_map,	/* Identity map signals. */
      	ident_map,	/*  - both ways. */
      	NULL,		/* No usage counter. */
      	NULL		/* Nothing after this in the list. */
      };
      
      static struct exec_domain *exec_domains = &default_exec_domain;
      static rwlock_t exec_domains_lock = RW_LOCK_UNLOCKED;
      
  28  static asmlinkage void no_lcall7(int segment, struct pt_regs * regs)
      {
        /*
         * This may have been a static linked SVr4 binary, so we would have the
         * personality set incorrectly.  Check to see whether SVr4 is available,
         * and use it, otherwise give the user a SEGV.
         */
  35  	set_personality(PER_SVR4);
      
      	if (current->exec_domain && current->exec_domain->handler
  38  	&& current->exec_domain->handler != no_lcall7) {
      		current->exec_domain->handler(segment, regs);
  40  		return;
      	}
      
      	send_sig(SIGSEGV, current, 1);
      }
      
  46  static struct exec_domain *lookup_exec_domain(unsigned long personality)
      {
      	unsigned long pers = personality & PER_MASK;
      	struct exec_domain *it;
      
      	read_lock(&exec_domains_lock);
  52  	for (it=exec_domains; it; it=it->next)
  53  		if (pers >= it->pers_low && pers <= it->pers_high) {
  54  			if (!try_inc_mod_count(it->module))
  55  				continue;
  56  			read_unlock(&exec_domains_lock);
  57  			return it;
      		}
  59  	read_unlock(&exec_domains_lock);
      
      	/* Should never get this far. */
      	printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers);
  63  	return NULL;
      }
      
  66  int register_exec_domain(struct exec_domain *it)
      {
      	struct exec_domain *tmp;
      
  70  	if (!it)
  71  		return -EINVAL;
  72  	if (it->next)
  73  		return -EBUSY;
      	write_lock(&exec_domains_lock);
  75  	for (tmp=exec_domains; tmp; tmp=tmp->next)
  76  		if (tmp == it) {
  77  			write_unlock(&exec_domains_lock);
  78  			return -EBUSY;
      		}
      	it->next = exec_domains;
      	exec_domains = it;
  82  	write_unlock(&exec_domains_lock);
  83  	return 0;
      }
      
  86  int unregister_exec_domain(struct exec_domain *it)
      {
      	struct exec_domain ** tmp;
      
      	tmp = &exec_domains;
      	write_lock(&exec_domains_lock);
  92  	while (*tmp) {
  93  		if (it == *tmp) {
      			*tmp = it->next;
      			it->next = NULL;
  96  			write_unlock(&exec_domains_lock);
  97  			return 0;
      		}
      		tmp = &(*tmp)->next;
      	}
 101  	write_unlock(&exec_domains_lock);
 102  	return -EINVAL;
      }
      
 105  void __set_personality(unsigned long personality)
      {
      	struct exec_domain *it, *prev;
      
      	it = lookup_exec_domain(personality);
 110  	if (it == current->exec_domain) {
      		current->personality = personality;
 112  		return;
      	}
 114  	if (!it)
 115  		return;
 116  	if (atomic_read(¤t->fs->count) != 1) {
      		struct fs_struct *new = copy_fs_struct(current->fs);
      		struct fs_struct *old;
 119  		if (!new) {
 120  			put_exec_domain(it);
 121  			return;
      		}
      		task_lock(current);
      		old = current->fs;
      		current->fs = new;
      		task_unlock(current);
      		put_fs_struct(old);
      	}
      	/*
      	 * At that point we are guaranteed to be the sole owner of
      	 * current->fs.
      	 */
      	current->personality = personality;
      	prev = current->exec_domain;
      	current->exec_domain = it;
      	set_fs_altroot();
 137  	put_exec_domain(prev);
      }
      
 140  asmlinkage long sys_personality(unsigned long personality)
      {
      	int ret = current->personality;
 143  	if (personality != 0xffffffff) {
 144  		set_personality(personality);
 145  		if (current->personality != personality)
      			ret = -EINVAL;
      	}
 148  	return ret;
      }
      
 151  int get_exec_domain_list(char * page)
      {
      	int len = 0;
      	struct exec_domain * e;
      
      	read_lock(&exec_domains_lock);
 157  	for (e=exec_domains; e && len < PAGE_SIZE - 80; e=e->next)
      		len += sprintf(page+len, "%d-%d\t%-16s\t[%s]\n",
      			e->pers_low, e->pers_high, e->name,
      			e->module ? e->module->name : "kernel");
 161  	read_unlock(&exec_domains_lock);
 162  	return len;
      }