/* Komentarze po polsku wykonali: Sławek Poreda i Marcin Mędelski-Guz */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/a.out.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/binfmts.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/shm.h>
#include <linux/personality.h>
#include <linux/elfcore.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <linux/config.h>
#define DLINFO_ITEMS 12
#include <linux/elf.h>
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs
* regs);
static int load_elf_library(int fd);
extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
extern void dump_thread(struct pt_regs *, struct user *);
/*
* If we don't support core dumping, then supply a NULL so
we
* don't even try.
*/
#ifdef USE_ELF_CORE_DUMP
static int elf_core_dump(long signr, struct pt_regs * regs);
#else
#define elf_core_dump NULL
#endif
#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_EXEC_PAGESIZE-1))
#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_EXEC_PAGESIZE-1))
static struct linux_binfmt elf_format = {
#ifndef MODULE
NULL, NULL, load_elf_binary, load_elf_library, elf_core_dump
#else
NULL, &mod_use_count_, load_elf_binary, load_elf_library,
elf_core_dump
#endif
};
/* set_brk przydziela pamięć o wielkości end-start od adresu danego
zm. start */
static void set_brk(unsigned long start, unsigned long end)
{
start = PAGE_ALIGN(start);
end = PAGE_ALIGN(end);
if (end <= start)
return;
do_mmap(NULL, start, end - start,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
}
/* We need to explicitly zero any fractional pages
after the data section (i.e. bss). This would
contain the junk from the file that should not
be in memory */
/* wypełnia zerami obszar danych nieinicjowanych wartościami z pliku
*/
static void padzero(unsigned long elf_bss)
{
unsigned long nbyte;
char * fpnt;
nbyte = elf_bss & (PAGE_SIZE-1);
if (nbyte) {
nbyte = PAGE_SIZE - nbyte;
/* FIXME: someone should investigate, why a bad binary
is allowed to bring a wrong elf_bss until
here,
and how to react. Suffice the plain return?
rossius@hrz.tu-chemnitz.de */
if (verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte))
{
return;
}
fpnt = (char *) elf_bss;
do {
put_user(0, fpnt++);
} while (--nbyte);
}
}
unsigned long * create_elf_tables(char *p, int argc, int envc,
struct elfhdr * exec,
unsigned long load_addr,
unsigned long interp_load_addr,
int ibcs)
{
unsigned long *argv, *envp, *dlinfo;
unsigned long *sp;
/*
* Force 16 byte alignment here for generality.
*/
/* sp to jest p wyrównane do 16 bajtów */
sp = (unsigned long *) (~15UL & (unsigned long) p);
/* zarezerwoawanie miejsca na nagłówek, jeśli jest podany
*/
/* w przeciwnym przypadku - miejsce tylko na znacznik jego
końca */
sp -= exec ? DLINFO_ITEMS*2 : 2;
dlinfo = sp;
/* zarezerwowanie miejsca na zmienne środowiskowe */
sp -= envc+1;
/* envp wskazuje na początek tablicy zmiennych środowiskowych
*/
envp = sp;
/* zarezerwowanie miejsca na argumenty */
sp -= argc+1;
/* argv wskazuje na początek tablicy parametrów wywołania
*/
argv = sp;
if (!ibcs) {
/* zapisanie wyliczonych przed chwilą wskaźników */
put_user(envp,--sp);
put_user(argv,--sp);
}
/* zapiusuje parę - identyfikator, wartość */
#define NEW_AUX_ENT(id, val) \
put_user ((id), dlinfo++); \
put_user ((val), dlinfo++)
/* jeśli jest nagłówek, to zapisujemy
go w przydzielone wcześniej miejsce, */
/* wskazywane przez dlinfo */
if (exec) { /* Put this here for an ELF program interpreter
*/
/* dwie następne linie
można skasować, eppnt nie jest nigdzie wykorzystywany !? */
struct elf_phdr * eppnt;
eppnt = (struct elf_phdr *) exec->e_phoff;
NEW_AUX_ENT (AT_PHDR, load_addr + exec->e_phoff); /*
nagłówki programu */
NEW_AUX_ENT (AT_PHENT, sizeof (struct elf_phdr));
/* wielkość nagłówka */
NEW_AUX_ENT (AT_PHNUM, exec->e_phnum); /* liczba nagłówków
*/
NEW_AUX_ENT (AT_PAGESZ, PAGE_SIZE); /* wielkość strony
*/
NEW_AUX_ENT (AT_BASE, interp_load_addr); /* adres
interpretatora elf */
NEW_AUX_ENT (AT_FLAGS, 0);
NEW_AUX_ENT (AT_ENTRY, (unsigned long) exec->e_entry);
/* początek kodu programu */
NEW_AUX_ENT (AT_UID, (unsigned long) current->uid);
NEW_AUX_ENT (AT_EUID, (unsigned long) current->euid);
/* efektywny UID */
NEW_AUX_ENT (AT_GID, (unsigned long) current->gid);
NEW_AUX_ENT (AT_EGID, (unsigned long) current->egid);
}
/* znacznik końca nagłówka */
NEW_AUX_ENT (AT_NULL, 0);
#undef NEW_AUX_ENT
/* zapamiętaj liczbę parametrów */
put_user((unsigned long)argc,--sp);
/* zapisanie do task_struct wskaźnika na argumenty wywołania
*/
current->mm->arg_start = (unsigned long) p;
/* zapisanie argumentów wywołania do obszaru pamięci użytkownika
*/
/* w postaci tablicy zmiennych typu char* */
while (argc-->0) {
put_user(p,argv++);
while (get_user(p++)) /* nothing */ ;
}
/* zapisanie znacznika końca tablicy parametrów */
put_user(0,argv);
/* zapisanie do task_struct wskaźnika na zmienne środowiskowe
*/
current->mm->arg_end = current->mm->env_start = (unsigned
long) p;
/* zapisanie zm. środowiskowych do obszaru pamięci użytkownika
*/
/* w postaci tablicy zmiennych typu char* */
while (envc-->0) {
put_user(p,envp++);
while (get_user(p++)) /* nothing */ ;
}
/* zapisanie znacznika końca tablicy zmiennych środowiskowych
*/
put_user(0,envp);
/* znacznik wskaźnika do końca zmiennych środowiskowych */
current->mm->env_end = (unsigned long) p;
return sp;
}
/* This is much more generalized than the library routine read function,
so we keep this separate. Technically the library
read function
is only provided so that we can read a.out libraries
that have
an ELF header */
/* wczytuje interpreter typu ELF */
static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
struct inode *
interpreter_inode,
unsigned long
*interp_load_addr)
{
struct file * file;
struct elf_phdr *elf_phdata = NULL;
struct elf_phdr *eppnt;
unsigned long load_addr;
int load_addr_set = 0;
int elf_exec_fileno;
int retval;
unsigned long last_bss, elf_bss;
unsigned long error;
int i;
elf_bss = 0;
last_bss = 0;
error = load_addr = 0;
/* First of all, some simple consistency checks */
if ((interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN) || /*
sprawdzamy, czy to plik wykonywalny */
!elf_check_arch(interp_elf_ex->e_machine) ||
/* czy na tę maszynę? */
(!interpreter_inode->i_op ||
!interpreter_inode->i_op->default_file_ops->mmap)){
/* czy system plików pozwala */
return ~0UL; /*
na stronicowanie plików */
}
/* Now read in all of the header information */
/* Czy nagłówki mieszczą na jednej stronie */
if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE)
return ~0UL;
/* zarezerwuj pamięć na nagłówki sekcji */
elf_phdata = (struct elf_phdr *)
kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum,
GFP_KERNEL);
if (!elf_phdata)
return ~0UL;
/*
* If the size of this structure has changed, then punt,
since
* we will be doing the wrong thing.
*/
/* sprawdza, czy nie zmienił się format pliku ELF (sic!)
*/
if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
{
kfree(elf_phdata);
return ~0UL;
}
/* wczytaj nagłówki sekcji */
retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff,
(char *) elf_phdata,
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum,
1);
if (retval < 0) {
kfree (elf_phdata);
return retval;
}
/* otwarcie pliku */
elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY);
if (elf_exec_fileno < 0) {
kfree(elf_phdata);
return ~0UL;
}
file = current->files->fd[elf_exec_fileno];
eppnt = elf_phdata;
/* przeglądanie nagłówków sekcji pliku */
for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
if (eppnt->p_type == PT_LOAD) { /* jeśli sekcja do
wczytania */
int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
int elf_prot = 0;
unsigned long vaddr = 0;
unsigned long k;
/* ustaw flagi ochrony segmentu sekcji
*/
if (eppnt->p_flags & PF_R) elf_prot
= PROT_READ;
if (eppnt->p_flags & PF_W) elf_prot
|= PROT_WRITE;
if (eppnt->p_flags & PF_X) elf_prot
|= PROT_EXEC;
if (interp_elf_ex->e_type == ET_EXEC ||
load_addr_set) {
elf_type |= MAP_FIXED;
vaddr = eppnt->p_vaddr;
}
/* odwzoruj odp. fragment pliku do pamięci
wirtualnej */
error = do_mmap(file,
load_addr + ELF_PAGESTART(vaddr),
eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr),
elf_prot,
elf_type,
eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
if (error > -1024UL) {
/* Real error */
sys_close(elf_exec_fileno);
kfree(elf_phdata);
return ~0UL;
}
if (!load_addr_set && interp_elf_ex->e_type
== ET_DYN) {
load_addr = error;
load_addr_set = 1;
}
/*
* Find the end of the file
mapping for this phdr, and keep
* track of the largest address we
see for this.
*/
/* wyliczamy obszar pamięci wirtualnej,
potrzebny na umieszczenie */
/* w nim wszystkich sekcji, pod odpowiednimi adresami */
k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
if (k > elf_bss) elf_bss = k;
/*
* Do the same thing for the memory
mapping - between
* elf_bss and last_bss is the bss
section.
*/
k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
if (k > last_bss) last_bss = k;
}
/* Now use mmap to map the library into memory. */
sys_close(elf_exec_fileno);
/*
* Now fill out the bss section. First pad the last
page up
* to the page boundary, and then perform a mmap to make
sure
* that there are zeromapped pages up to and including the
last
* bss page.
*/
/* wyzeruj końcówkę ostatniej strony przydzielonej na kod
i dane incjowane z pliku */
padzero(elf_bss);
elf_bss = ELF_PAGESTART(elf_bss + ELF_EXEC_PAGESIZE - 1);
/* What we have mapped so far */
/* Map the last of the bss segment */
/* jeśli potrzeba przydziel dodatkowe strony na dane niezaincjalizowane
*/
if (last_bss > elf_bss)
do_mmap(NULL, elf_bss, last_bss-elf_bss,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
kfree(elf_phdata);
*interp_load_addr = load_addr;
return ((unsigned long) interp_elf_ex->e_entry) + load_addr;
}
static unsigned long load_aout_interp(struct exec * interp_ex,
struct inode * interpreter_inode)
{
int retval;
unsigned long elf_entry;
current->mm->brk = interp_ex->a_bss +
(current->mm->end_data = interp_ex->a_data +
(current->mm->end_code = interp_ex->a_text));
elf_entry = interp_ex->a_entry;
/* jeśli to jest a.out z 32 bajtowym nagłówkiem to */
/* wczytaj odpowiedni jego fragment do pamięci (bez nagłówka)
*/
if (N_MAGIC(*interp_ex) == OMAGIC) {
do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
retval = read_exec(interpreter_inode, 32, (char
*) 0,
interp_ex->a_text+interp_ex->a_data,
0);
} /* jeśli bez nagłówka, to wczytaj cały plik */
else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex)
== QMAGIC) {
do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
retval = read_exec(interpreter_inode,
N_TXTOFF(*interp_ex)
,
(char *) N_TXTADDR(*interp_ex),
interp_ex->a_text+interp_ex->a_data,
0);
} else
retval = -1;
/* przydziel pamięć na zmienne niezainicjowane */
if (retval >= 0)
do_mmap(NULL, ELF_PAGESTART(interp_ex->a_text
+ interp_ex->a_data + ELF_EXEC_PAGESIZE - 1),
interp_ex->a_bss,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
if (retval < 0) return ~0UL;
return elf_entry;
}
/*
* These are the functions used to load ELF style executables
and shared
* libraries. There is no binary dependent code anywhere
else.
*/
#define INTERPRETER_NONE 0
#define INTERPRETER_AOUT 1
#define INTERPRETER_ELF 2
static inline int
do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs *
regs)
{
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct file * file;
struct exec interp_ex;
struct inode *interpreter_inode;
unsigned long load_addr;
int load_addr_set = 0;
unsigned int interpreter_type = INTERPRETER_NONE;
unsigned char ibcs2_interpreter;
int i;
int old_fs;
int error;
struct elf_phdr * elf_ppnt, *elf_phdata;
int elf_exec_fileno;
unsigned long elf_bss, k, elf_brk;
int retval;
char * elf_interpreter;
unsigned long elf_entry, interp_load_addr = 0;
int status;
unsigned long start_code, end_code, end_data;
unsigned long elf_stack;
char passed_fileno[6];
ibcs2_interpreter = 0;
status = 0;
load_addr = 0;
/* wskaźnik do nagłówka pliku */
elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header
*/
/* sprawdź czy to jest format ELF */
if (elf_ex.e_ident[0] != 0x7f ||
strncmp(&elf_ex.e_ident[1], "ELF",3)
!= 0) {
return -ENOEXEC;
}
/* First of all, some simple consistency checks */
if ((elf_ex.e_type != ET_EXEC &&
elf_ex.e_type != ET_DYN) || /* sprawdzenie
typu pliku */
(! elf_check_arch(elf_ex.e_machine)) ||
/* czy to jest kod na tę maszynę? */
(!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops
||
!bprm->inode->i_op->default_file_ops->mmap)){
return -ENOEXEC;
}
/* Now read in all of the header information */
/* przydział pamięci na nagłowki sekcji pliku ELF */
elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize
*
elf_ex.e_phnum, GFP_KERNEL);
if (elf_phdata == NULL) {
return -ENOMEM;
}
/* wczytaj nagłówki */
retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *)
elf_phdata,
elf_ex.e_phentsize * elf_ex.e_phnum,
1);
if (retval < 0) {
kfree (elf_phdata);
return retval;
}
elf_ppnt = elf_phdata;
elf_bss = 0;
elf_brk = 0;
elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); /* otwórz
plik */
if (elf_exec_fileno < 0) {
kfree (elf_phdata);
return elf_exec_fileno;
}
file = current->files->fd[elf_exec_fileno];
elf_stack = ~0UL;
elf_interpreter = NULL;
start_code = ~0UL;
end_code = 0;
end_data = 0;
for(i=0;i < elf_ex.e_phnum; i++){
if (elf_ppnt->p_type == PT_INTERP) {
/* Sprawdź kolejny nagłówek. Czy potrzebny jest
interpretator? */
if ( elf_interpreter != NULL )
/* jeśli we wcześniejszej iteracji już wczytaliśmy
interpr. */
{ /* zwolnij pamięć i zwróć błąd */
kfree (elf_phdata);
kfree(elf_interpreter);
sys_close(elf_exec_fileno);
return -EINVAL;
}
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/
/* przydział pamięci na nazwę interpretatora */
elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
GFP_KERNEL);
if (elf_interpreter == NULL) {
kfree (elf_phdata);
sys_close(elf_exec_fileno);
return -ENOMEM;
}
/* wczytanie nazwy interpretatora z pliku do zmiennej
elf_interpreter */
retval = read_exec(bprm->inode,elf_ppnt->p_offset,
elf_interpreter,
elf_ppnt->p_filesz,
1);
/* If the program interpreter is one of these two,
then assume an iBCS2 image. Otherwise
assume
a native linux image. */
if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") ==
0 ||
strcmp(elf_interpreter,"/usr/lib/ld.so.1")
== 0)
ibcs2_interpreter = 1;
#if 0
printk("Using ELF interpreter %s\n", elf_interpreter);
#endif
if (retval >= 0) {
old_fs = get_fs(); /* This could probably be
optimized */
/* wstaw do rejestru fs, aktualną wartość ds
- segment danych jądra */
/* jest to konieczne, bo funkcje plikowe operują
na rejestrze fs */
set_fs(get_ds());
/* otwórz i-węzeł interpretatora */
retval = open_namei(elf_interpreter, 0, 0,
&interpreter_inode,
NULL);
set_fs(old_fs);
}
/* wczytaj nagłówek interpretatora do bprm->buf, na
miejsce nagłówka */
/* uruchamianego pliku */
if (retval >= 0)
retval = read_exec(interpreter_inode,0,bprm->buf,128,
1);
if (retval >= 0) {
interp_ex = *((struct exec *) bprm->buf);
/* exec-header */
interp_elf_ex = *((struct elfhdr *) bprm->buf);
/* exec-header */
}
/* w razie błędu - koniec */
if (retval < 0) {
kfree (elf_phdata);
kfree(elf_interpreter);
sys_close(elf_exec_fileno);
return retval;
}
}
/* następny nagłówek */
elf_ppnt++;
}
/* Some simple consistency checks for the interpreter */
/* jeśli potrzebny interpreter */
if (elf_interpreter){
interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
/* Now figure out which format our binary is */
/* czy interpreter jest typu a.out? */
/* jeśli formatem pliku nie jest a.out to interpreter_type
= INTERPRETER_ELF */
if ((N_MAGIC(interp_ex) != OMAGIC) &&
(N_MAGIC(interp_ex) != ZMAGIC) &&
(N_MAGIC(interp_ex) != QMAGIC))
interpreter_type = INTERPRETER_ELF;
/* czy interpreter jest typu elf */
/* jeśli nie to wyzeruj znacznik, że to elf */
if (interp_elf_ex.e_ident[0] != 0x7f ||
strncmp(&interp_elf_ex.e_ident[1],
"ELF",3) != 0)
interpreter_type &= ~INTERPRETER_ELF;
/* jeśli ani elf, ani a.out to błąd */
if (!interpreter_type)
{
kfree(elf_interpreter);
kfree(elf_phdata);
sys_close(elf_exec_fileno);
return -ELIBBAD;
}
}
/* OK, we are done with that, now set up the arg stuff,
and then start this sucker up */
/* jeśli wykonywany program nie jest powłoką dla innego
*/
if (!bprm->sh_bang) {
char * passed_p;
if (interpreter_type == INTERPRETER_AOUT) {
/* elf_exec_fileno
- deskryptor uruchomianageo pliku */
sprintf(passed_fileno, "%d", elf_exec_fileno);
passed_p = passed_fileno;
if (elf_interpreter) {
/* przekaż jako pierwszy parametr
do interpretatora ten deskryptor, */
/* a za nim parametry naszego uruchamianego
programu */
bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2);
bprm->argc++; /* zwiększ liczbę
parametrów o 1 */
}
}
/* jeśli zabrakło pamięci to błąd */
if (!bprm->p) {
if (elf_interpreter) {
kfree(elf_interpreter);
}
kfree (elf_phdata);
sys_close(elf_exec_fileno);
return -E2BIG;
}
}
if (flush_old_exec(bprm))
return -ENOMEM;
/* OK, This is the point of no return */
/* inicjacja pól task_struct */
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->start_mmap = ELF_START_MMAP;
current->mm->mmap = NULL;
elf_entry = (unsigned long) elf_ex.e_entry;
/* Do this so that we can load the interpreter, if need be.
We will
change some of these later */
current->mm->rss = 0;
/* kopiuje fragment pamieci zawierajacy argumenty i środowisko
z pamięci jądra */
/* do przestrzeni adresowej procesu */
bprm->p = setup_arg_pages(bprm->p, bprm);
current->mm->start_stack = bprm->p;
/* Now we do a little grungy work by mmaping the ELF image
into
the correct location in memory. At this
point, we assume that
the image should be loaded at fixed address,
not at a variable
address. */
old_fs = get_fs();
set_fs(get_ds());
/* dla każdego nagłówka sekcji */
for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum;
i++, elf_ppnt++) {
/* jeśli sekcja ma być załadowana do pamięci */
if (elf_ppnt->p_type == PT_LOAD) {
int elf_prot = 0;
/* ustawia parametry ochrony segmentu pamięci danej
sekcji */
if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
/* odwzoruj odpowiedni fragmnet pliku do pamięci */
error = do_mmap(file,
ELF_PAGESTART(elf_ppnt->p_vaddr),
(elf_ppnt->p_filesz +
ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
elf_prot,
(MAP_FIXED | MAP_PRIVATE |
MAP_DENYWRITE | MAP_EXECUTABLE),
(elf_ppnt->p_offset -
ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
#ifdef LOW_ELF_STACK
if (ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
elf_stack = ELF_PAGESTART(elf_ppnt->p_vaddr);
#endif
/* jeśli zmienna load_addr jeszcze nie zainicjowany
to ją ustaw */
if (!load_addr_set) {
load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
load_addr_set = 1;
}
/* wyliczamy obszar pamięci wirtualnej, potrzebny
na dane nieinicjowane */
/* wszystkich sekcji, oraz adres początku kodu */
k = elf_ppnt->p_vaddr;
if (k < start_code) start_code = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
if (k > elf_bss) elf_bss = k;
#if 1
if ((elf_ppnt->p_flags & PF_X) && end_code
< k)
#else
if ( !(elf_ppnt->p_flags & PF_W) && end_code
< k)
#endif
end_code = k;
if (end_data < k) end_data = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
if (k > elf_brk) elf_brk = k;
}
}
set_fs(old_fs);
if (elf_interpreter) {
/* jeśli interpretatorem jest program typu a.out */
if (interpreter_type & 1)
elf_entry = load_aout_interp(&interp_ex,
interpreter_inode);
/* jeśli interpretatorem jest program typu elf */
else if (interpreter_type & 2)
elf_entry = load_elf_interp(&interp_elf_ex,
interpreter_inode,
&interp_load_addr);
/* zwróć i-węzeł */
iput(interpreter_inode);
kfree(elf_interpreter);
/* jeśli nie udało się wczytać interpretatora to zrób sobie
błąd segmentacji */
if (elf_entry == ~0UL) {
printk("Unable to load interpreter\n");
kfree(elf_phdata);
send_sig(SIGSEGV, current, 0);
return 0;
}
}
kfree(elf_phdata);
if (interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno);
/* jeśli jest to plik wykonywalny z systemu SV4 to ustaw
mu odpowiednią osobowość */
current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
/* zmniejsz, jeśli trzeba, ilość odwołań do domeny wykonywania
poprzedniego procesu */
if (current->exec_domain && current->exec_domain->use_count)
(*current->exec_domain->use_count)--;
/* zmiejsz, jeśli trzeba, ilość odwołań do modułu danego
formatu */
if (current->binfmt && current->binfmt->use_count)
(*current->binfmt->use_count)--;
/* znajdź domenę wykonywania, rozpoznającą ten typ osobowości
*/
current->exec_domain = lookup_exec_domain(current->personality);
/* ustaw odpowiedni format binarny */
current->binfmt = &elf_format;
/* zwiększ, jeśli trzeba, ilość odwołań do domeny wykonywania
dla nowego procesu */
if (current->exec_domain && current->exec_domain->use_count)
(*current->exec_domain->use_count)++;
/* zwiększ, jeśli trzeba, ilość odwołań do modułu danego
formatu - dla nowego procesu */
if (current->binfmt && current->binfmt->use_count)
(*current->binfmt->use_count)++;
#ifndef VM_STACK_FLAGS
current->executable = bprm->inode;
bprm->inode->i_count++;
#endif
#ifdef LOW_ELF_STACK
current->start_stack = bprm->p = elf_stack - 4;
#endif
/* inicjacja pól w task_struct */
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
current->flags &= ~PF_FORKNOEXEC;
bprm->p = (unsigned long)
/* tworzenie tablic zmiennych środowiskowych i parametrów
wywołania */
create_elf_tables((char *)bprm->p,
bprm->argc,
bprm->envc,
(interpreter_type == INTERPRETER_ELF ? &elf_ex
: NULL),
load_addr,
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
/* usuń deskryptor pliku, sztucznie tam wstawiony wcześniej
*/
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) +
1;
/* inicjacja pól w task_struct */
current->mm->start_brk = current->mm->brk = elf_brk;
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;
/* Calling set_brk effectively mmaps the pages that we need
for the bss and break
sections */
/* przydziel pamięć na zmienne niezaincjowane */
set_brk(elf_bss, elf_brk);
/* wyzeruj zmienne niezaincjowane */
padzero(elf_bss);
#if 0
printk("(start_brk) %x\n" , current->mm->start_brk);
printk("(end_code) %x\n" , current->mm->end_code);
printk("(start_code) %x\n" , current->mm->start_code);
printk("(end_data) %x\n" , current->mm->end_data);
printk("(start_stack) %x\n" , current->mm->start_stack);
printk("(brk) %x\n" , current->mm->brk);
#endif
/* jeśli jest to kod wykonywlny dla systemu SVR4 */
if ( current->personality == PER_SVR4 )
{
/* Why this, you ask??? Well SVr4 maps page 0 as read-only,
and some applications "depend" upon this
behavior.
Since we do not have the power to recompile
these, we
emulate the SVr4 behavior. Sigh.
*/
/* ustaw zerową stronę jako tylko do odczytu */
error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
}
#ifdef ELF_PLAT_INIT
/*
* The ABI may specify that certain registers be set up in
special
* ways (on i386 %edx is the address of a DT_FINI function,
for
* example. This macro performs whatever initialization
to
* the regs structure is required.
*/
/* inicjacja rejestrów procesora, jeśli to konieczne */
ELF_PLAT_INIT(regs);
#endif
/* uruchom wątek */
start_thread(regs, elf_entry, bprm->p);
/* jeśli proces jest śledzony, zablokuj go - praca krokowa
*/
if (current->flags & PF_PTRACED)
send_sig(SIGTRAP, current, 0);
return 0;
}
static int
load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
int retval;
MOD_INC_USE_COUNT;
retval = do_load_elf_binary(bprm, regs);
MOD_DEC_USE_COUNT;
return retval;
}
/* This is really simpleminded and specialized - we are loading
an
a.out library that is given an ELF header. */
static inline int
do_load_elf_library(int fd){
struct file * file;
struct elfhdr elf_ex;
struct elf_phdr *elf_phdata = NULL;
struct inode * inode;
unsigned long len;
int elf_bss;
int retval;
unsigned long bss;
int error;
int i,j, k;
len = 0;
file = current->files->fd[fd];
inode = file->f_inode;
elf_bss = 0;
if (!file || !file->f_op)
return -EACCES;
/* seek to the beginning of the file */
if (file->f_op->lseek) {
if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
return -ENOEXEC;
} else
file->f_pos = 0;
set_fs(KERNEL_DS);
/* wczytywanie naglówka biblioteki (elf_ex) do pamięci jądra
*/
/* read wczytuje do segmentu wskazywanego
przez rejestr FS procesora, */
/* stąd trzeba go najpierw ustawić na segment danych jądra
*/
error = file->f_op->read(inode, file, (char *) &elf_ex,
sizeof(elf_ex));
set_fs(USER_DS);
if (error != sizeof(elf_ex))
return -ENOEXEC;
/* sprawdzenie formatu pliku */
if (elf_ex.e_ident[0] != 0x7f ||
strncmp(&elf_ex.e_ident[1], "ELF",3)
!= 0)
return -ENOEXEC;
/* First of all, some simple consistency checks */
if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
/* typ pliku */
!elf_check_arch(elf_ex.e_machine) ||
/* czy na tę maszynę */
(!inode->i_op || !inode->i_op->default_file_ops->mmap))
return -ENOEXEC;
/* Now read in all of the header information */
if (sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE)
return -ENOEXEC;
/* przydział pamięci na nagłówki sekcji */
elf_phdata = (struct elf_phdr *)
kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
if (elf_phdata == NULL)
return -ENOMEM;
/* wczytanie nagłówków sekcji - wcześniej sprawdzone było,
*/
/* czy jest ich nie więcej niż dwie
*/
retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
sizeof(struct elf_phdr) * elf_ex.e_phnum,
1);
j = 0; /* zlicz sekcje, które trzeba załadować */
for(i=0; i<elf_ex.e_phnum; i++)
if ((elf_phdata + i)->p_type == PT_LOAD) j++;
/* powinna być tylko jedna taka sekcja */
if (j != 1) {
kfree(elf_phdata);
return -ENOEXEC;
}
/* znajdź odp. sekcję */
while(elf_phdata->p_type != PT_LOAD) elf_phdata++;
/* Now use mmap to map the library into memory. */
/* teraz odwzoruj plik z kodem biblioteki do pamięci wirtualnej
*/
error = do_mmap(file,
ELF_PAGESTART(elf_phdata->p_vaddr),
(elf_phdata->p_filesz +
ELF_PAGEOFFSET(elf_phdata->p_vaddr)),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
(elf_phdata->p_offset -
ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
/* znajdź wielkość pamięci na kod oraz dane zainicjowane
*/
k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
if (k > elf_bss) elf_bss = k;
/* zm. error jest równa adresowi odwzorowanego pliku lub
mniejsza od zera */
if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) {
kfree(elf_phdata);
return error;
}
/* wyzeruj "końcówkę" ostatniej strony przydzielonej na kod
i dane inicjowane */
padzero(elf_bss);
len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr+
ELF_EXEC_PAGESIZE - 1);
bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
if (bss > len) /* jeśli trzeba, przydziel dodatkowe strony
na dane nieinicjowane */
do_mmap(NULL, len, bss-len,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
kfree(elf_phdata);
return 0;
}
static int load_elf_library(int fd)
{
int retval;
MOD_INC_USE_COUNT;
retval = do_load_elf_library(fd);
MOD_DEC_USE_COUNT;
return retval;
}
/*
* Note that some platforms still use traditional core dumps
and not
* the ELF core dump. Each platform can select it as
appropriate.
*/
#ifdef USE_ELF_CORE_DUMP
/*
* ELF core dumper
*
* Modelled on fs/exec.c:aout_core_dump()
* Jeremy Fitzhardinge <jeremy@sw.oz.au>
*/
/*
* These are the only things you should do on a core-file:
use only these
* functions to write out all the necessary info.
*/
static int dump_write(struct file *file, const void *addr, int
nr)
{
return file->f_op->write(file->f_inode, file, addr, nr) ==
nr;
}
static int dump_seek(struct file *file, off_t off)
{
if (file->f_op->lseek) {
if (file->f_op->lseek(file->f_inode, file, off, 0) != off)
return 0;
} else
file->f_pos = off;
return 1;
}
/*
* Decide whether a segment is worth dumping; default is yes
to be
* sure (missing info is worse than too much; etc).
* Personally I'd include everything, and use the coredump
limit...
*
* I think we should skip something. But I am not sure how.
H.J.
*/
static inline int maydump(struct vm_area_struct *vma)
{
if (!(vma->vm_flags & (VM_READ|VM_EXEC)))
return 0;
#if 1
if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN))
return 1;
if (vma->vm_flags & (VM_READ|VM_EXEC|VM_EXECUTABLE|VM_SHARED))
return 0;
#endif
return 1;
}
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
/* An ELF note in memory */
struct memelfnote
{
const char *name;
int type;
unsigned int datasz;
void *data;
};
static int notesize(struct memelfnote *en)
{
int sz;
sz = sizeof(struct elf_note);
sz += roundup(strlen(en->name), 4);
sz += roundup(en->datasz, 4);
return sz;
}
/* #define DEBUG */
#ifdef DEBUG
static void dump_regs(const char *str, elf_greg_t *r)
{
int i;
static const char *regs[] = { "ebx", "ecx", "edx", "esi",
"edi", "ebp",
"eax",
"ds", "es", "fs", "gs",
"orig_eax",
"eip", "cs",
"efl",
"uesp", "ss"};
printk("Registers: %s\n", str);
for(i = 0; i < ELF_NGREG; i++)
{
unsigned long val = r[i];
printk(" %-2d %-5s=%08lx %lu\n", i, regs[i],
val, val);
}
}
#endif
#define DUMP_WRITE(addr, nr) \
do { if (!dump_write(file, (addr), (nr))) return 0; } while(0)
#define DUMP_SEEK(off) \
do { if (!dump_seek(file, (off))) return 0; } while(0)
static int writenote(struct memelfnote *men, struct file *file)
{
struct elf_note en;
en.n_namesz = strlen(men->name);
en.n_descsz = men->datasz;
en.n_type = men->type;
DUMP_WRITE(&en, sizeof(en));
DUMP_WRITE(men->name, en.n_namesz);
/* XXX - cast from long long to long to avoid need for libgcc.a
*/
DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX
*/
DUMP_WRITE(men->data, men->datasz);
DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX
*/
return 1;
}
#undef DUMP_WRITE
#undef DUMP_SEEK
#define DUMP_WRITE(addr, nr) \
if (!dump_write(&file, (addr), (nr))) \
goto close_coredump;
#define DUMP_SEEK(off) \
if (!dump_seek(&file, (off))) \
goto close_coredump;
/*
* Actual dumper
*
* This is a two-pass process; first we find the offsets of
the bits,
* and then they are actually written out. If we run
out of core limit
* we just truncate.
*/
static int elf_core_dump(long signr, struct pt_regs * regs)
{
int has_dumped = 0;
struct file file;
struct inode *inode;
unsigned short fs;
char corefile[6+sizeof(current->comm)];
int segs;
int i;
size_t size;
struct vm_area_struct *vma;
struct elfhdr elf;
off_t offset = 0, dataoff;
int limit = current->rlim[RLIMIT_CORE].rlim_cur;
int numnote = 4;
struct memelfnote notes[4];
struct elf_prstatus prstatus; /* NT_PRSTATUS */
elf_fpregset_t fpu; /* NT_PRFPREG */
struct elf_prpsinfo psinfo; /* NT_PRPSINFO */
if (!current->dumpable || limit < PAGE_SIZE || current->mm->count
!= 1)
return 0;
current->dumpable = 0;
#ifndef CONFIG_BINFMT_ELF
MOD_INC_USE_COUNT;
#endif
/* Count what's needed to dump, up to the limit of coredump
size */
segs = 0;
size = 0;
for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next)
{
if (maydump(vma))
{
int sz = vma->vm_end-vma->vm_start;
if (size+sz >= limit)
break;
else
size += sz;
}
segs++;
}
#ifdef DEBUG
printk("elf_core_dump: %d segs taking %d bytes\n", segs,
size);
#endif
/* Set up header */
memcpy(elf.e_ident, ELFMAG, SELFMAG);
elf.e_ident[EI_CLASS] = ELF_CLASS;
elf.e_ident[EI_DATA] = ELF_DATA;
elf.e_ident[EI_VERSION] = EV_CURRENT;
memset(elf.e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf.e_type = ET_CORE;
elf.e_machine = ELF_ARCH;
elf.e_version = EV_CURRENT;
elf.e_entry = 0;
elf.e_phoff = sizeof(elf);
elf.e_shoff = 0;
elf.e_flags = 0;
elf.e_ehsize = sizeof(elf);
elf.e_phentsize = sizeof(struct elf_phdr);
elf.e_phnum = segs+1; /* Include notes */
elf.e_shentsize = 0;
elf.e_shnum = 0;
elf.e_shstrndx = 0;
fs = get_fs();
set_fs(KERNEL_DS);
memcpy(corefile,"core.",5);
#if 0
memcpy(corefile+5,current->comm,sizeof(current->comm));
#else
corefile[4] = '\0';
#endif
if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL))
{
inode = NULL;
goto end_coredump;
}
if (!S_ISREG(inode->i_mode))
goto end_coredump;
if (!inode->i_op || !inode->i_op->default_file_ops)
goto end_coredump;
file.f_mode = 3;
file.f_flags = 0;
file.f_count = 1;
file.f_inode = inode;
file.f_pos = 0;
file.f_reada = 0;
file.f_op = inode->i_op->default_file_ops;
if (file.f_op->open)
if (file.f_op->open(inode,&file))
goto end_coredump;
if (!file.f_op->write)
goto close_coredump;
has_dumped = 1;
current->flags |= PF_DUMPCORE;
DUMP_WRITE(&elf, sizeof(elf));
offset += sizeof(elf); /* Elf header */
offset += (segs+1) * sizeof(struct elf_phdr); /* Program
headers */
/*
* Set up the notes in similar form to SVR4 core dumps made
* with info from their /proc.
*/
memset(&psinfo, 0, sizeof(psinfo));
memset(&prstatus, 0, sizeof(prstatus));
notes[0].name = "CORE";
notes[0].type = NT_PRSTATUS;
notes[0].datasz = sizeof(prstatus);
notes[0].data = &prstatus;
prstatus.pr_info.si_signo = prstatus.pr_cursig = signr;
prstatus.pr_sigpend = current->signal;
prstatus.pr_sighold = current->blocked;
psinfo.pr_pid = prstatus.pr_pid = current->pid;
psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid;
psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp;
psinfo.pr_sid = prstatus.pr_sid = current->session;
prstatus.pr_utime.tv_sec = CT_TO_SECS(current->utime);
prstatus.pr_utime.tv_usec = CT_TO_USECS(current->utime);
prstatus.pr_stime.tv_sec = CT_TO_SECS(current->stime);
prstatus.pr_stime.tv_usec = CT_TO_USECS(current->stime);
prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->cutime);
prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->cutime);
prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->cstime);
prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->cstime);
/*
* This transfers the registers from regs into the standard
* coredump arrangement, whatever that is.
*/
#ifdef ELF_CORE_COPY_REGS
ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
#else
if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
{
printk("sizeof(elf_gregset_t) (%d) != sizeof(struct pt_regs)
(%d)\n",
sizeof(elf_gregset_t), sizeof(struct pt_regs));
}
else
*(struct pt_regs *)&prstatus.pr_reg = *regs;
#endif
#ifdef DEBUG
dump_regs("Passed in regs", (elf_greg_t *)regs);
dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg);
#endif
notes[1].name = "CORE";
notes[1].type = NT_PRPSINFO;
notes[1].datasz = sizeof(psinfo);
notes[1].data = &psinfo;
psinfo.pr_state = current->state;
psinfo.pr_sname = (current->state < 0 || current->state
> 5) ? '.' : "RSDZTD"[current->state];
psinfo.pr_zomb = psinfo.pr_sname == 'Z';
psinfo.pr_nice = current->priority-15;
psinfo.pr_flag = current->flags;
psinfo.pr_uid = current->uid;
psinfo.pr_gid = current->gid;
{
int i, len;
set_fs(fs);
len = current->mm->arg_end - current->mm->arg_start;
len = (len >= ELF_PRARGSZ-1) ? ELF_PRARGSZ-1 : len;
memcpy_fromfs(&psinfo.pr_psargs,
(const char *)current->mm->arg_start,
len);
for(i = 0; i < len; i++)
if (psinfo.pr_psargs[i] == 0)
psinfo.pr_psargs[i] = ' ';
psinfo.pr_psargs[len] = 0;
set_fs(KERNEL_DS);
}
strncpy(psinfo.pr_fname, current->comm, sizeof(psinfo.pr_fname));
notes[2].name = "CORE";
notes[2].type = NT_TASKSTRUCT;
notes[2].datasz = sizeof(*current);
notes[2].data = current;
/* Try to dump the fpu. */
prstatus.pr_fpvalid = dump_fpu (regs, &fpu);
if (!prstatus.pr_fpvalid)
{
numnote--;
}
else
{
notes[3].name = "CORE";
notes[3].type = NT_PRFPREG;
notes[3].datasz = sizeof(fpu);
notes[3].data = &fpu;
}
/* Write notes phdr entry */
{
struct elf_phdr phdr;
int sz = 0;
for(i = 0; i < numnote; i++)
sz += notesize(¬es[i]);
phdr.p_type = PT_NOTE;
phdr.p_offset = offset;
phdr.p_vaddr = 0;
phdr.p_paddr = 0;
phdr.p_filesz = sz;
phdr.p_memsz = 0;
phdr.p_flags = 0;
phdr.p_align = 0;
offset += phdr.p_filesz;
DUMP_WRITE(&phdr, sizeof(phdr));
}
/* Page-align dumped data */
dataoff = offset = roundup(offset, PAGE_SIZE);
/* Write program headers for segments dump */
for(vma = current->mm->mmap, i = 0;
i < segs && vma != NULL; vma = vma->vm_next)
{
struct elf_phdr phdr;
size_t sz;
i++;
sz = vma->vm_end - vma->vm_start;
phdr.p_type = PT_LOAD;
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
phdr.p_filesz = maydump(vma) ? sz : 0;
phdr.p_memsz = sz;
offset += phdr.p_filesz;
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W;
if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X;
phdr.p_align = PAGE_SIZE;
DUMP_WRITE(&phdr, sizeof(phdr));
}
for(i = 0; i < numnote; i++)
if (!writenote(¬es[i], &file))
goto close_coredump;
set_fs(fs);
DUMP_SEEK(dataoff);
for(i = 0, vma = current->mm->mmap;
i < segs && vma != NULL;
vma = vma->vm_next) {
unsigned long addr = vma->vm_start;
unsigned long len = vma->vm_end - vma->vm_start;
i++;
if (!maydump(vma))
continue;
#ifdef DEBUG
printk("elf_core_dump: writing %08lx %lx\n", addr, len);
#endif
DUMP_WRITE((void *)addr, len);
}
if ((off_t) file.f_pos != offset) {
/* Sanity check */
printk("elf_core_dump: file.f_pos (%ld) != offset (%ld)\n",
(off_t) file.f_pos,
offset);
}
close_coredump:
if (file.f_op->release)
file.f_op->release(inode,&file);
end_coredump:
set_fs(fs);
iput(inode);
#ifndef CONFIG_BINFMT_ELF
MOD_DEC_USE_COUNT;
#endif
return has_dumped;
}
#endif /* USE_ELF_CORE_DUMP */
int init_elf_binfmt(void)
{
return register_binfmt(&elf_format);
}
#ifdef MODULE
int init_module(void)
{
/* Install the COFF, ELF and XOUT loaders.
* N.B. We *rely* on the table being the right size with
the
* right number of free slots...
*/
return init_elf_binfmt();
}
void cleanup_module( void)
{
/* Remove the COFF and ELF loaders. */
unregister_binfmt(&elf_format);
}
#endif