2005-04-17 02:20:36 +04:00
/****************************************************************************/
/*
* linux / fs / binfmt_flat . c
*
* Copyright ( C ) 2000 - 2003 David McCullough < davidm @ snapgear . com >
* Copyright ( C ) 2002 Greg Ungerer < gerg @ snapgear . com >
* Copyright ( C ) 2002 SnapGear , by Paul Dale < pauli @ snapgear . com >
* Copyright ( C ) 2000 , 2001 Lineo , by David McCullough < davidm @ lineo . com >
* based heavily on :
*
* linux / fs / binfmt_aout . c :
* Copyright ( C ) 1991 , 1992 , 1996 Linus Torvalds
* linux / fs / binfmt_flat . c for 2.0 kernel
* Copyright ( C ) 1998 Kenneth Albanowski < kjahds @ kjahds . com >
* JAN / 99 - - coded full program relocation ( gerg @ snapgear . com )
*/
# include <linux/module.h>
# include <linux/config.h>
# include <linux/kernel.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/string.h>
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/stat.h>
# include <linux/fcntl.h>
# include <linux/ptrace.h>
# include <linux/user.h>
# include <linux/slab.h>
# include <linux/binfmts.h>
# include <linux/personality.h>
# include <linux/init.h>
# include <linux/flat.h>
2006-03-25 14:08:24 +03:00
# include <linux/syscalls.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/unaligned.h>
# include <asm/cacheflush.h>
/****************************************************************************/
#if 0
# define DEBUG 1
# endif
# ifdef DEBUG
# define DBG_FLT(a...) printk(a)
# else
# define DBG_FLT(a...)
# endif
# define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */
# define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */
struct lib_info {
struct {
unsigned long start_code ; /* Start of text segment */
unsigned long start_data ; /* Start of data segment */
unsigned long start_brk ; /* End of data segment */
unsigned long text_len ; /* Length of text segment */
unsigned long entry ; /* Start address for this module */
unsigned long build_date ; /* When this one was compiled */
short loaded ; /* Has this library been loaded? */
} lib_list [ MAX_SHARED_LIBS ] ;
} ;
# ifdef CONFIG_BINFMT_SHARED_FLAT
static int load_flat_shared_library ( int id , struct lib_info * p ) ;
# endif
static int load_flat_binary ( struct linux_binprm * , struct pt_regs * regs ) ;
static int flat_core_dump ( long signr , struct pt_regs * regs , struct file * file ) ;
static struct linux_binfmt flat_format = {
. module = THIS_MODULE ,
. load_binary = load_flat_binary ,
. core_dump = flat_core_dump ,
. min_coredump = PAGE_SIZE
} ;
/****************************************************************************/
/*
* Routine writes a core dump image in the current directory .
* Currently only a stub - function .
*/
static int flat_core_dump ( long signr , struct pt_regs * regs , struct file * file )
{
printk ( " Process %s:%d received signr %d and should have core dumped \n " ,
current - > comm , current - > pid , ( int ) signr ) ;
return ( 1 ) ;
}
/****************************************************************************/
/*
* create_flat_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 .
*/
static unsigned long create_flat_tables (
unsigned long pp ,
struct linux_binprm * bprm )
{
unsigned long * argv , * envp ;
unsigned long * sp ;
char * p = ( char * ) pp ;
int argc = bprm - > argc ;
int envc = bprm - > envc ;
char dummy ;
sp = ( unsigned long * ) ( ( - ( unsigned long ) sizeof ( char * ) ) & ( unsigned long ) p ) ;
sp - = envc + 1 ;
envp = sp ;
sp - = argc + 1 ;
argv = sp ;
flat_stack_align ( sp ) ;
if ( flat_argvp_envp_on_stack ( ) ) {
- - sp ; put_user ( ( unsigned long ) envp , sp ) ;
- - sp ; put_user ( ( unsigned long ) argv , sp ) ;
}
put_user ( argc , - - sp ) ;
current - > mm - > arg_start = ( unsigned long ) p ;
while ( argc - - > 0 ) {
put_user ( ( unsigned long ) p , argv + + ) ;
do {
get_user ( dummy , p ) ; p + + ;
} while ( dummy ) ;
}
put_user ( ( unsigned long ) NULL , argv ) ;
current - > mm - > arg_end = current - > mm - > env_start = ( unsigned long ) p ;
while ( envc - - > 0 ) {
put_user ( ( unsigned long ) p , envp ) ; envp + + ;
do {
get_user ( dummy , p ) ; p + + ;
} while ( dummy ) ;
}
put_user ( ( unsigned long ) NULL , envp ) ;
current - > mm - > env_end = ( unsigned long ) p ;
return ( unsigned long ) sp ;
}
/****************************************************************************/
# ifdef CONFIG_BINFMT_ZFLAT
# include <linux/zlib.h>
# define LBUFSIZE 4000
/* gzip flag byte */
# define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
# define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
# define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
# define ORIG_NAME 0x08 /* bit 3 set: original file name present */
# define COMMENT 0x10 /* bit 4 set: file comment present */
# define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
# define RESERVED 0xC0 /* bit 6,7: reserved */
static int decompress_exec (
struct linux_binprm * bprm ,
unsigned long offset ,
char * dst ,
long len ,
int fd )
{
unsigned char * buf ;
z_stream strm ;
loff_t fpos ;
int ret , retval ;
DBG_FLT ( " decompress_exec(offset=%x,buf=%x,len=%x) \n " , ( int ) offset , ( int ) dst , ( int ) len ) ;
memset ( & strm , 0 , sizeof ( strm ) ) ;
strm . workspace = kmalloc ( zlib_inflate_workspacesize ( ) , GFP_KERNEL ) ;
if ( strm . workspace = = NULL ) {
DBG_FLT ( " binfmt_flat: no memory for decompress workspace \n " ) ;
return - ENOMEM ;
}
buf = kmalloc ( LBUFSIZE , GFP_KERNEL ) ;
if ( buf = = NULL ) {
DBG_FLT ( " binfmt_flat: no memory for read buffer \n " ) ;
retval = - ENOMEM ;
goto out_free ;
}
/* Read in first chunk of data and parse gzip header. */
fpos = offset ;
ret = bprm - > file - > f_op - > read ( bprm - > file , buf , LBUFSIZE , & fpos ) ;
strm . next_in = buf ;
strm . avail_in = ret ;
strm . total_in = 0 ;
retval = - ENOEXEC ;
/* Check minimum size -- gzip header */
if ( ret < 10 ) {
DBG_FLT ( " binfmt_flat: file too small? \n " ) ;
goto out_free_buf ;
}
/* Check gzip magic number */
if ( ( buf [ 0 ] ! = 037 ) | | ( ( buf [ 1 ] ! = 0213 ) & & ( buf [ 1 ] ! = 0236 ) ) ) {
DBG_FLT ( " binfmt_flat: unknown compression magic? \n " ) ;
goto out_free_buf ;
}
/* Check gzip method */
if ( buf [ 2 ] ! = 8 ) {
DBG_FLT ( " binfmt_flat: unknown compression method? \n " ) ;
goto out_free_buf ;
}
/* Check gzip flags */
if ( ( buf [ 3 ] & ENCRYPTED ) | | ( buf [ 3 ] & CONTINUATION ) | |
( buf [ 3 ] & RESERVED ) ) {
DBG_FLT ( " binfmt_flat: unknown flags? \n " ) ;
goto out_free_buf ;
}
ret = 10 ;
if ( buf [ 3 ] & EXTRA_FIELD ) {
ret + = 2 + buf [ 10 ] + ( buf [ 11 ] < < 8 ) ;
if ( unlikely ( LBUFSIZE = = ret ) ) {
DBG_FLT ( " binfmt_flat: buffer overflow (EXTRA)? \n " ) ;
goto out_free_buf ;
}
}
if ( buf [ 3 ] & ORIG_NAME ) {
for ( ; ret < LBUFSIZE & & ( buf [ ret ] ! = 0 ) ; ret + + )
;
if ( unlikely ( LBUFSIZE = = ret ) ) {
DBG_FLT ( " binfmt_flat: buffer overflow (ORIG_NAME)? \n " ) ;
goto out_free_buf ;
}
}
if ( buf [ 3 ] & COMMENT ) {
for ( ; ret < LBUFSIZE & & ( buf [ ret ] ! = 0 ) ; ret + + )
;
if ( unlikely ( LBUFSIZE = = ret ) ) {
DBG_FLT ( " binfmt_flat: buffer overflow (COMMENT)? \n " ) ;
goto out_free_buf ;
}
}
strm . next_in + = ret ;
strm . avail_in - = ret ;
strm . next_out = dst ;
strm . avail_out = len ;
strm . total_out = 0 ;
if ( zlib_inflateInit2 ( & strm , - MAX_WBITS ) ! = Z_OK ) {
DBG_FLT ( " binfmt_flat: zlib init failed? \n " ) ;
goto out_free_buf ;
}
while ( ( ret = zlib_inflate ( & strm , Z_NO_FLUSH ) ) = = Z_OK ) {
ret = bprm - > file - > f_op - > read ( bprm - > file , buf , LBUFSIZE , & fpos ) ;
if ( ret < = 0 )
break ;
if ( ret > = ( unsigned long ) - 4096 )
break ;
len - = ret ;
strm . next_in = buf ;
strm . avail_in = ret ;
strm . total_in = 0 ;
}
if ( ret < 0 ) {
DBG_FLT ( " binfmt_flat: decompression failed (%d), %s \n " ,
ret , strm . msg ) ;
goto out_zlib ;
}
retval = 0 ;
out_zlib :
zlib_inflateEnd ( & strm ) ;
out_free_buf :
kfree ( buf ) ;
out_free :
kfree ( strm . workspace ) ;
out :
return retval ;
}
# endif /* CONFIG_BINFMT_ZFLAT */
/****************************************************************************/
static unsigned long
calc_reloc ( unsigned long r , struct lib_info * p , int curid , int internalp )
{
unsigned long addr ;
int id ;
unsigned long start_brk ;
unsigned long start_data ;
unsigned long text_len ;
unsigned long start_code ;
# ifdef CONFIG_BINFMT_SHARED_FLAT
if ( r = = 0 )
id = curid ; /* Relocs of 0 are always self referring */
else {
id = ( r > > 24 ) & 0xff ; /* Find ID for this reloc */
r & = 0x00ffffff ; /* Trim ID off here */
}
if ( id > = MAX_SHARED_LIBS ) {
printk ( " BINFMT_FLAT: reference 0x%x to shared library %d " ,
( unsigned ) r , id ) ;
goto failed ;
}
if ( curid ! = id ) {
if ( internalp ) {
printk ( " BINFMT_FLAT: reloc address 0x%x not in same module "
" (%d != %d) " , ( unsigned ) r , curid , id ) ;
goto failed ;
} else if ( ! p - > lib_list [ id ] . loaded & &
load_flat_shared_library ( id , p ) > ( unsigned long ) - 4096 ) {
printk ( " BINFMT_FLAT: failed to load library %d " , id ) ;
goto failed ;
}
/* Check versioning information (i.e. time stamps) */
if ( p - > lib_list [ id ] . build_date & & p - > lib_list [ curid ] . build_date & &
p - > lib_list [ curid ] . build_date < p - > lib_list [ id ] . build_date ) {
printk ( " BINFMT_FLAT: library %d is younger than %d " , id , curid ) ;
goto failed ;
}
}
# else
id = 0 ;
# endif
start_brk = p - > lib_list [ id ] . start_brk ;
start_data = p - > lib_list [ id ] . start_data ;
start_code = p - > lib_list [ id ] . start_code ;
text_len = p - > lib_list [ id ] . text_len ;
if ( ! flat_reloc_valid ( r , start_brk - start_data + text_len ) ) {
printk ( " BINFMT_FLAT: reloc outside program 0x%x (0 - 0x%x/0x%x) " ,
( int ) r , ( int ) ( start_brk - start_code ) , ( int ) text_len ) ;
goto failed ;
}
if ( r < text_len ) /* In text segment */
addr = r + start_code ;
else /* In data segment */
addr = r - text_len + start_data ;
/* Range checked already above so doing the range tests is redundant...*/
return ( addr ) ;
failed :
printk ( " , killing %s! \n " , current - > comm ) ;
send_sig ( SIGSEGV , current , 0 ) ;
return RELOC_FAILED ;
}
/****************************************************************************/
void old_reloc ( unsigned long rl )
{
# ifdef DEBUG
char * segment [ ] = { " TEXT " , " DATA " , " BSS " , " *UNKNOWN* " } ;
# endif
flat_v2_reloc_t r ;
unsigned long * ptr ;
r . value = rl ;
# if defined(CONFIG_COLDFIRE)
ptr = ( unsigned long * ) ( current - > mm - > start_code + r . reloc . offset ) ;
# else
ptr = ( unsigned long * ) ( current - > mm - > start_data + r . reloc . offset ) ;
# endif
# ifdef DEBUG
printk ( " Relocation of variable at DATASEG+%x "
" (address %p, currently %x) into segment %s \n " ,
r . reloc . offset , ptr , ( int ) * ptr , segment [ r . reloc . type ] ) ;
# endif
switch ( r . reloc . type ) {
case OLD_FLAT_RELOC_TYPE_TEXT :
* ptr + = current - > mm - > start_code ;
break ;
case OLD_FLAT_RELOC_TYPE_DATA :
* ptr + = current - > mm - > start_data ;
break ;
case OLD_FLAT_RELOC_TYPE_BSS :
* ptr + = current - > mm - > end_data ;
break ;
default :
printk ( " BINFMT_FLAT: Unknown relocation type=%x \n " , r . reloc . type ) ;
break ;
}
# ifdef DEBUG
printk ( " Relocation became %x \n " , ( int ) * ptr ) ;
# endif
}
/****************************************************************************/
static int load_flat_file ( struct linux_binprm * bprm ,
struct lib_info * libinfo , int id , unsigned long * extra_stack )
{
struct flat_hdr * hdr ;
unsigned long textpos = 0 , datapos = 0 , result ;
unsigned long realdatastart = 0 ;
unsigned long text_len , data_len , bss_len , stack_len , flags ;
unsigned long memp = 0 ; /* for finding the brk area */
unsigned long extra , rlim ;
unsigned long * reloc = 0 , * rp ;
struct inode * inode ;
int i , rev , relocs = 0 ;
loff_t fpos ;
unsigned long start_code , end_code ;
2006-03-25 14:08:24 +03:00
int ret ;
int exec_fileno ;
2005-04-17 02:20:36 +04:00
hdr = ( ( struct flat_hdr * ) bprm - > buf ) ; /* exec-header */
inode = bprm - > file - > f_dentry - > d_inode ;
text_len = ntohl ( hdr - > data_start ) ;
data_len = ntohl ( hdr - > data_end ) - ntohl ( hdr - > data_start ) ;
bss_len = ntohl ( hdr - > bss_end ) - ntohl ( hdr - > data_end ) ;
stack_len = ntohl ( hdr - > stack_size ) ;
if ( extra_stack ) {
stack_len + = * extra_stack ;
* extra_stack = stack_len ;
}
relocs = ntohl ( hdr - > reloc_count ) ;
flags = ntohl ( hdr - > flags ) ;
rev = ntohl ( hdr - > rev ) ;
2006-01-10 09:59:37 +03:00
if ( strncmp ( hdr - > magic , " bFLT " , 4 ) ) {
2005-04-17 02:20:36 +04:00
/*
* because a lot of people do not manage to produce good
* flat binaries , we leave this printk to help them realise
* the problem . We only print the error if its not a script file
*/
if ( strncmp ( hdr - > magic , " #! " , 2 ) )
2006-01-10 09:59:37 +03:00
printk ( " BINFMT_FLAT: bad header magic \n " ) ;
2006-03-25 14:08:24 +03:00
ret = - ENOEXEC ;
goto err ;
2006-01-10 09:59:37 +03:00
}
if ( flags & FLAT_FLAG_KTRACE )
printk ( " BINFMT_FLAT: Loading file: %s \n " , bprm - > filename ) ;
if ( rev ! = FLAT_VERSION & & rev ! = OLD_FLAT_VERSION ) {
printk ( " BINFMT_FLAT: bad flat file version 0x%x (supported 0x%x and 0x%x) \n " , rev , FLAT_VERSION , OLD_FLAT_VERSION ) ;
2006-03-25 14:08:24 +03:00
ret = - ENOEXEC ;
goto err ;
2005-04-17 02:20:36 +04:00
}
/* Don't allow old format executables to use shared libraries */
if ( rev = = OLD_FLAT_VERSION & & id ! = 0 ) {
printk ( " BINFMT_FLAT: shared libraries are not available before rev 0x%x \n " ,
( int ) FLAT_VERSION ) ;
2006-03-25 14:08:24 +03:00
ret = - ENOEXEC ;
goto err ;
2005-04-17 02:20:36 +04:00
}
/*
* fix up the flags for the older format , there were all kinds
* of endian hacks , this only works for the simple cases
*/
if ( rev = = OLD_FLAT_VERSION & & flat_old_ram_flag ( flags ) )
flags = FLAT_FLAG_RAM ;
# ifndef CONFIG_BINFMT_ZFLAT
if ( flags & ( FLAT_FLAG_GZIP | FLAT_FLAG_GZDATA ) ) {
printk ( " Support for ZFLAT executables is not enabled. \n " ) ;
2006-03-25 14:08:24 +03:00
ret = - ENOEXEC ;
goto err ;
2005-04-17 02:20:36 +04:00
}
# endif
/*
* Check initial limits . This avoids letting people circumvent
* size limits imposed on them by creating programs with large
* arrays in the data or bss .
*/
rlim = current - > signal - > rlim [ RLIMIT_DATA ] . rlim_cur ;
if ( rlim > = RLIM_INFINITY )
rlim = ~ 0 ;
2006-03-25 14:08:24 +03:00
if ( data_len + bss_len > rlim ) {
ret = - ENOMEM ;
goto err ;
}
/* check file descriptor */
exec_fileno = get_unused_fd ( ) ;
if ( exec_fileno < 0 ) {
ret = - EMFILE ;
goto err ;
}
get_file ( bprm - > file ) ;
fd_install ( exec_fileno , bprm - > file ) ;
2005-04-17 02:20:36 +04:00
/* Flush all traces of the currently running executable */
if ( id = = 0 ) {
result = flush_old_exec ( bprm ) ;
2006-03-25 14:08:24 +03:00
if ( result ) {
ret = result ;
goto err_close ;
}
2005-04-17 02:20:36 +04:00
/* OK, This is the point of no return */
set_personality ( PER_LINUX ) ;
}
/*
* calculate the extra space we need to map in
*/
extra = max ( bss_len + stack_len , relocs * sizeof ( unsigned long ) ) ;
/*
* there are a couple of cases here , the separate code / data
* case , and then the fully copied to RAM case which lumps
* it all together .
*/
if ( ( flags & ( FLAT_FLAG_RAM | FLAT_FLAG_GZIP ) ) = = 0 ) {
/*
* this should give us a ROM ptr , but if it doesn ' t we don ' t
* really care
*/
DBG_FLT ( " BINFMT_FLAT: ROM mapping of file (we hope) \n " ) ;
down_write ( & current - > mm - > mmap_sem ) ;
2005-09-02 04:42:52 +04:00
textpos = do_mmap ( bprm - > file , 0 , text_len , PROT_READ | PROT_EXEC , MAP_PRIVATE , 0 ) ;
2005-04-17 02:20:36 +04:00
up_write ( & current - > mm - > mmap_sem ) ;
if ( ! textpos | | textpos > = ( unsigned long ) - 4096 ) {
if ( ! textpos )
textpos = ( unsigned long ) - ENOMEM ;
printk ( " Unable to mmap process text, errno %d \n " , ( int ) - textpos ) ;
2006-03-25 14:08:24 +03:00
ret = textpos ;
goto err_close ;
2005-04-17 02:20:36 +04:00
}
down_write ( & current - > mm - > mmap_sem ) ;
realdatastart = do_mmap ( 0 , 0 , data_len + extra +
MAX_SHARED_LIBS * sizeof ( unsigned long ) ,
2005-06-07 01:46:32 +04:00
PROT_READ | PROT_WRITE | PROT_EXEC , MAP_PRIVATE , 0 ) ;
2005-04-17 02:20:36 +04:00
up_write ( & current - > mm - > mmap_sem ) ;
if ( realdatastart = = 0 | | realdatastart > = ( unsigned long ) - 4096 ) {
if ( ! realdatastart )
realdatastart = ( unsigned long ) - ENOMEM ;
printk ( " Unable to allocate RAM for process data, errno %d \n " ,
( int ) - datapos ) ;
do_munmap ( current - > mm , textpos , text_len ) ;
2006-03-25 14:08:24 +03:00
ret = realdatastart ;
goto err_close ;
2005-04-17 02:20:36 +04:00
}
datapos = realdatastart + MAX_SHARED_LIBS * sizeof ( unsigned long ) ;
DBG_FLT ( " BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x \n " ,
( int ) ( data_len + bss_len + stack_len ) , ( int ) datapos ) ;
fpos = ntohl ( hdr - > data_start ) ;
# ifdef CONFIG_BINFMT_ZFLAT
if ( flags & FLAT_FLAG_GZDATA ) {
result = decompress_exec ( bprm , fpos , ( char * ) datapos ,
data_len + ( relocs * sizeof ( unsigned long ) ) , 0 ) ;
} else
# endif
{
result = bprm - > file - > f_op - > read ( bprm - > file , ( char * ) datapos ,
data_len + ( relocs * sizeof ( unsigned long ) ) , & fpos ) ;
}
if ( result > = ( unsigned long ) - 4096 ) {
printk ( " Unable to read data+bss, errno %d \n " , ( int ) - result ) ;
do_munmap ( current - > mm , textpos , text_len ) ;
do_munmap ( current - > mm , realdatastart , data_len + extra ) ;
2006-03-25 14:08:24 +03:00
ret = result ;
goto err_close ;
2005-04-17 02:20:36 +04:00
}
reloc = ( unsigned long * ) ( datapos + ( ntohl ( hdr - > reloc_start ) - text_len ) ) ;
memp = realdatastart ;
} else {
down_write ( & current - > mm - > mmap_sem ) ;
textpos = do_mmap ( 0 , 0 , text_len + data_len + extra +
MAX_SHARED_LIBS * sizeof ( unsigned long ) ,
2005-06-07 01:46:32 +04:00
PROT_READ | PROT_EXEC | PROT_WRITE , MAP_PRIVATE , 0 ) ;
2005-04-17 02:20:36 +04:00
up_write ( & current - > mm - > mmap_sem ) ;
if ( ! textpos | | textpos > = ( unsigned long ) - 4096 ) {
if ( ! textpos )
textpos = ( unsigned long ) - ENOMEM ;
printk ( " Unable to allocate RAM for process text/data, errno %d \n " ,
( int ) - textpos ) ;
2006-03-25 14:08:24 +03:00
ret = textpos ;
goto err_close ;
2005-04-17 02:20:36 +04:00
}
realdatastart = textpos + ntohl ( hdr - > data_start ) ;
datapos = realdatastart + MAX_SHARED_LIBS * sizeof ( unsigned long ) ;
reloc = ( unsigned long * ) ( textpos + ntohl ( hdr - > reloc_start ) +
MAX_SHARED_LIBS * sizeof ( unsigned long ) ) ;
memp = textpos ;
# ifdef CONFIG_BINFMT_ZFLAT
/*
* load it all in and treat it like a RAM load from now on
*/
if ( flags & FLAT_FLAG_GZIP ) {
result = decompress_exec ( bprm , sizeof ( struct flat_hdr ) ,
( ( ( char * ) textpos ) + sizeof ( struct flat_hdr ) ) ,
( text_len + data_len + ( relocs * sizeof ( unsigned long ) )
- sizeof ( struct flat_hdr ) ) ,
0 ) ;
memmove ( ( void * ) datapos , ( void * ) realdatastart ,
data_len + ( relocs * sizeof ( unsigned long ) ) ) ;
} else if ( flags & FLAT_FLAG_GZDATA ) {
fpos = 0 ;
result = bprm - > file - > f_op - > read ( bprm - > file ,
( char * ) textpos , text_len , & fpos ) ;
if ( result < ( unsigned long ) - 4096 )
result = decompress_exec ( bprm , text_len , ( char * ) datapos ,
data_len + ( relocs * sizeof ( unsigned long ) ) , 0 ) ;
}
else
# endif
{
fpos = 0 ;
result = bprm - > file - > f_op - > read ( bprm - > file ,
( char * ) textpos , text_len , & fpos ) ;
if ( result < ( unsigned long ) - 4096 ) {
fpos = ntohl ( hdr - > data_start ) ;
result = bprm - > file - > f_op - > read ( bprm - > file , ( char * ) datapos ,
data_len + ( relocs * sizeof ( unsigned long ) ) , & fpos ) ;
}
}
if ( result > = ( unsigned long ) - 4096 ) {
printk ( " Unable to read code+data+bss, errno %d \n " , ( int ) - result ) ;
do_munmap ( current - > mm , textpos , text_len + data_len + extra +
MAX_SHARED_LIBS * sizeof ( unsigned long ) ) ;
2006-03-25 14:08:24 +03:00
ret = result ;
goto err_close ;
2005-04-17 02:20:36 +04:00
}
}
if ( flags & FLAT_FLAG_KTRACE )
printk ( " Mapping is %x, Entry point is %x, data_start is %x \n " ,
( int ) textpos , 0x00ffffff & ntohl ( hdr - > entry ) , ntohl ( hdr - > data_start ) ) ;
/* The main program needs a little extra setup in the task structure */
start_code = textpos + sizeof ( struct flat_hdr ) ;
end_code = textpos + text_len ;
if ( id = = 0 ) {
current - > mm - > start_code = start_code ;
current - > mm - > end_code = end_code ;
current - > mm - > start_data = datapos ;
current - > mm - > end_data = datapos + data_len ;
/*
* set up the brk stuff , uses any slack left in data / bss / stack
* allocation . We put the brk after the bss ( between the bss
* and stack ) like other platforms .
*/
current - > mm - > start_brk = datapos + data_len + bss_len ;
current - > mm - > brk = ( current - > mm - > start_brk + 3 ) & ~ 3 ;
current - > mm - > context . end_brk = memp + ksize ( ( void * ) memp ) - stack_len ;
}
if ( flags & FLAT_FLAG_KTRACE )
printk ( " %s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x \n " ,
id ? " Lib " : " Load " , bprm - > filename ,
( int ) start_code , ( int ) end_code ,
( int ) datapos ,
( int ) ( datapos + data_len ) ,
( int ) ( datapos + data_len ) ,
( int ) ( ( ( datapos + data_len + bss_len ) + 3 ) & ~ 3 ) ) ;
text_len - = sizeof ( struct flat_hdr ) ; /* the real code len */
/* Store the current module values into the global library structure */
libinfo - > lib_list [ id ] . start_code = start_code ;
libinfo - > lib_list [ id ] . start_data = datapos ;
libinfo - > lib_list [ id ] . start_brk = datapos + data_len + bss_len ;
libinfo - > lib_list [ id ] . text_len = text_len ;
libinfo - > lib_list [ id ] . loaded = 1 ;
libinfo - > lib_list [ id ] . entry = ( 0x00ffffff & ntohl ( hdr - > entry ) ) + textpos ;
libinfo - > lib_list [ id ] . build_date = ntohl ( hdr - > build_date ) ;
/*
* We just load the allocations into some temporary memory to
* help simplify all this mumbo jumbo
*
* We ' ve got two different sections of relocation entries .
* The first is the GOT which resides at the begining of the data segment
* and is terminated with a - 1. This one can be relocated in place .
* The second is the extra relocation entries tacked after the image ' s
* data segment . These require a little more processing as the entry is
* really an offset into the image which contains an offset into the
* image .
*/
if ( flags & FLAT_FLAG_GOTPIC ) {
for ( rp = ( unsigned long * ) datapos ; * rp ! = 0xffffffff ; rp + + ) {
unsigned long addr ;
if ( * rp ) {
addr = calc_reloc ( * rp , libinfo , id , 0 ) ;
2006-03-25 14:08:24 +03:00
if ( addr = = RELOC_FAILED ) {
ret = - ENOEXEC ;
goto err_close ;
}
2005-04-17 02:20:36 +04:00
* rp = addr ;
}
}
}
/*
* Now run through the relocation entries .
* We ' ve got to be careful here as C + + produces relocatable zero
* entries in the constructor and destructor tables which are then
* tested for being not zero ( which will always occur unless we ' re
* based from address zero ) . This causes an endless loop as __start
* is at zero . The solution used is to not relocate zero addresses .
* This has the negative side effect of not allowing a global data
* reference to be statically initialised to _stext ( I ' ve moved
* __start to address 4 so that is okay ) .
*/
if ( rev > OLD_FLAT_VERSION ) {
for ( i = 0 ; i < relocs ; i + + ) {
unsigned long addr , relval ;
/* Get the address of the pointer to be
relocated ( of course , the address has to be
relocated first ) . */
relval = ntohl ( reloc [ i ] ) ;
addr = flat_get_relocate_addr ( relval ) ;
rp = ( unsigned long * ) calc_reloc ( addr , libinfo , id , 1 ) ;
2006-03-25 14:08:24 +03:00
if ( rp = = ( unsigned long * ) RELOC_FAILED ) {
ret = - ENOEXEC ;
goto err_close ;
}
2005-04-17 02:20:36 +04:00
/* Get the pointer's value. */
addr = flat_get_addr_from_rp ( rp , relval , flags ) ;
if ( addr ! = 0 ) {
/*
* Do the relocation . PIC relocs in the data section are
* already in target order
*/
if ( ( flags & FLAT_FLAG_GOTPIC ) = = 0 )
addr = ntohl ( addr ) ;
addr = calc_reloc ( addr , libinfo , id , 0 ) ;
2006-03-25 14:08:24 +03:00
if ( addr = = RELOC_FAILED ) {
ret = - ENOEXEC ;
goto err_close ;
}
2005-04-17 02:20:36 +04:00
/* Write back the relocated pointer. */
flat_put_addr_at_rp ( rp , addr , relval ) ;
}
}
} else {
for ( i = 0 ; i < relocs ; i + + )
old_reloc ( ntohl ( reloc [ i ] ) ) ;
}
flush_icache_range ( start_code , end_code ) ;
/* zero the BSS, BRK and stack areas */
memset ( ( void * ) ( datapos + data_len ) , 0 , bss_len +
( memp + ksize ( ( void * ) memp ) - stack_len - /* end brk */
libinfo - > lib_list [ id ] . start_brk ) + /* start brk */
stack_len ) ;
return 0 ;
2006-03-25 14:08:24 +03:00
err_close :
sys_close ( exec_fileno ) ;
err :
return ret ;
2005-04-17 02:20:36 +04:00
}
/****************************************************************************/
# ifdef CONFIG_BINFMT_SHARED_FLAT
/*
* Load a shared library into memory . The library gets its own data
* segment ( including bss ) but not argv / argc / environ .
*/
static int load_flat_shared_library ( int id , struct lib_info * libs )
{
struct linux_binprm bprm ;
int res ;
char buf [ 16 ] ;
/* Create the file name */
sprintf ( buf , " /lib/lib%d.so " , id ) ;
/* Open the file up */
bprm . filename = buf ;
bprm . file = open_exec ( bprm . filename ) ;
res = PTR_ERR ( bprm . file ) ;
if ( IS_ERR ( bprm . file ) )
return res ;
res = prepare_binprm ( & bprm ) ;
if ( res < = ( unsigned long ) - 4096 )
res = load_flat_file ( & bprm , libs , id , NULL ) ;
if ( bprm . file ) {
allow_write_access ( bprm . file ) ;
fput ( bprm . file ) ;
bprm . file = NULL ;
}
return ( res ) ;
}
# endif /* CONFIG_BINFMT_SHARED_FLAT */
/****************************************************************************/
/*
* These are the functions used to load flat style executables and shared
* libraries . There is no binary dependent code anywhere else .
*/
static int load_flat_binary ( struct linux_binprm * bprm , struct pt_regs * regs )
{
struct lib_info libinfo ;
unsigned long p = bprm - > p ;
unsigned long stack_len ;
unsigned long start_addr ;
unsigned long * sp ;
int res ;
int i , j ;
memset ( & libinfo , 0 , sizeof ( libinfo ) ) ;
/*
* We have to add the size of our arguments to our stack size
* otherwise it ' s too easy for users to create stack overflows
* by passing in a huge argument list . And yes , we have to be
* pedantic and include space for the argv / envp array as it may have
* a lot of entries .
*/
# define TOP_OF_ARGS (PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *))
stack_len = TOP_OF_ARGS - bprm - > p ; /* the strings */
stack_len + = ( bprm - > argc + 1 ) * sizeof ( char * ) ; /* the argv array */
stack_len + = ( bprm - > envc + 1 ) * sizeof ( char * ) ; /* the envp array */
res = load_flat_file ( bprm , & libinfo , 0 , & stack_len ) ;
if ( res > ( unsigned long ) - 4096 )
return res ;
/* Update data segment pointers for all libraries */
for ( i = 0 ; i < MAX_SHARED_LIBS ; i + + )
if ( libinfo . lib_list [ i ] . loaded )
for ( j = 0 ; j < MAX_SHARED_LIBS ; j + + )
( - ( j + 1 ) ) [ ( unsigned long * ) ( libinfo . lib_list [ i ] . start_data ) ] =
( libinfo . lib_list [ j ] . loaded ) ?
libinfo . lib_list [ j ] . start_data : UNLOADED_LIB ;
compute_creds ( bprm ) ;
current - > flags & = ~ PF_FORKNOEXEC ;
set_binfmt ( & flat_format ) ;
p = ( ( current - > mm - > context . end_brk + stack_len + 3 ) & ~ 3 ) - 4 ;
DBG_FLT ( " p=%x \n " , ( int ) p ) ;
/* copy the arg pages onto the stack, this could be more efficient :-) */
for ( i = TOP_OF_ARGS - 1 ; i > = bprm - > p ; i - - )
* ( char * ) - - p =
( ( char * ) page_address ( bprm - > page [ i / PAGE_SIZE ] ) ) [ i % PAGE_SIZE ] ;
sp = ( unsigned long * ) create_flat_tables ( p , bprm ) ;
/* Fake some return addresses to ensure the call chain will
* initialise library in order for us . We are required to call
* lib 1 first , then 2 , . . . and finally the main program ( id 0 ) .
*/
start_addr = libinfo . lib_list [ 0 ] . entry ;
# ifdef CONFIG_BINFMT_SHARED_FLAT
for ( i = MAX_SHARED_LIBS - 1 ; i > 0 ; i - - ) {
if ( libinfo . lib_list [ i ] . loaded ) {
/* Push previos first to call address */
- - sp ; put_user ( start_addr , sp ) ;
start_addr = libinfo . lib_list [ i ] . entry ;
}
}
# endif
/* Stash our initial stack pointer into the mm structure */
current - > mm - > start_stack = ( unsigned long ) sp ;
DBG_FLT ( " start_thread(regs=0x%x, entry=0x%x, start_stack=0x%x) \n " ,
( int ) regs , ( int ) start_addr , ( int ) current - > mm - > start_stack ) ;
start_thread ( regs , start_addr , current - > mm - > start_stack ) ;
if ( current - > ptrace & PT_PTRACED )
send_sig ( SIGTRAP , current , 0 ) ;
return 0 ;
}
/****************************************************************************/
static int __init init_flat_binfmt ( void )
{
return register_binfmt ( & flat_format ) ;
}
static void __exit exit_flat_binfmt ( void )
{
unregister_binfmt ( & flat_format ) ;
}
/****************************************************************************/
core_initcall ( init_flat_binfmt ) ;
module_exit ( exit_flat_binfmt ) ;
/****************************************************************************/