Commit e5fe0c52 authored by pbrook's avatar pbrook
Browse files

bFLT loader (for uClinux binaries).


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1951 c046a42c-6fe2-441c-8c8c-71466251a162
parent ac62f715
......@@ -171,7 +171,12 @@ LDFLAGS+=-p
main.o: CFLAGS+=-p
endif
OBJS= elfload.o main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o
OBJS= main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o \
elfload.o linuxload.o
ifdef TARGET_HAS_BFLT
OBJS+= flatload.o
endif
ifeq ($(TARGET_ARCH), i386)
OBJS+= vm86.o
endif
......
......@@ -773,6 +773,7 @@ echo "/* Automatically generated by configure - do not modify */" > $config_h
echo "include ../config-host.mak" >> $config_mak
echo "#include \"../config-host.h\"" >> $config_h
bflt="no"
interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
......@@ -787,6 +788,7 @@ elif test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
echo "TARGET_ARCH=arm" >> $config_mak
echo "#define TARGET_ARCH \"arm\"" >> $config_h
echo "#define TARGET_ARM 1" >> $config_h
bflt="yes"
elif test "$target_cpu" = "sparc" ; then
echo "TARGET_ARCH=sparc" >> $config_mak
echo "#define TARGET_ARCH \"sparc\"" >> $config_h
......@@ -842,6 +844,10 @@ if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
fi
if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then
echo "TARGET_HAS_BFLT=yes" >> $config_mak
echo "#define TARGET_HAS_BFLT 1" >> $config_h
fi
# sdl defines
if test "$target_user_only" = "no"; then
......
......@@ -3,7 +3,6 @@
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
......@@ -59,19 +58,19 @@ static uint32_t get_elf_hwcap(void)
#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_386
/* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program
starts %edx contains a pointer to a function which might be
registered using `atexit'. This provides a mean for the
dynamic linker to call DT_FINI functions for shared libraries
that have been loaded before the code runs.
A value of 0 tells we have no such handler. */
#define ELF_PLAT_INIT(_r) _r->edx = 0
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
regs->esp = infop->start_stack;
regs->eip = infop->entry;
/* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program
starts %edx contains a pointer to a function which might be
registered using `atexit'. This provides a mean for the
dynamic linker to call DT_FINI functions for shared libraries
that have been loaded before the code runs.
A value of 0 tells we have no such handler. */
regs->edx = 0;
}
#define USE_ELF_CORE_DUMP
......@@ -93,8 +92,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#endif
#define ELF_ARCH EM_ARM
#define ELF_PLAT_INIT(_r) _r->ARM_r0 = 0
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
target_long stack = infop->start_stack;
......@@ -107,7 +104,9 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
regs->ARM_r2 = tgetl(stack + 8); /* envp */
regs->ARM_r1 = tgetl(stack + 4); /* envp */
/* XXX: it seems that r0 is zeroed after ! */
// regs->ARM_r0 = tgetl(stack); /* argc */
regs->ARM_r0 = 0;
/* For uClinux PIC binaries. */
regs->ARM_r10 = infop->start_data;
}
#define USE_ELF_CORE_DUMP
......@@ -142,9 +141,6 @@ enum
#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_SPARC
/*XXX*/
#define ELF_PLAT_INIT(_r)
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
regs->tstate = 0;
......@@ -163,9 +159,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_SPARC
/*XXX*/
#define ELF_PLAT_INIT(_r)
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
regs->psr = 0;
......@@ -192,20 +185,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#endif
#define ELF_ARCH EM_PPC
/* Note that isn't exactly what regular kernel does
* but this is what the ABI wants and is needed to allow
* execution of PPC BSD programs.
*/
#define ELF_PLAT_INIT(_r) \
do { \
target_ulong *pos = (target_ulong *)bprm->p, tmp = 1; \
_r->gpr[3] = bprm->argc; \
_r->gpr[4] = (unsigned long)++pos; \
for (; tmp != 0; pos++) \
tmp = ldl(pos); \
_r->gpr[5] = (unsigned long)pos; \
} while (0)
/*
* We need to put in some extra aux table entries to tell glibc what
* the cache block size is, so it can use the dcbz instruction safely.
......@@ -239,9 +218,22 @@ do { \
static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
{
target_ulong pos = infop->start_stack;
target_ulong tmp;
_regs->msr = 1 << MSR_PR; /* Set user mode */
_regs->gpr[1] = infop->start_stack;
_regs->nip = infop->entry;
/* Note that isn't exactly what regular kernel does
* but this is what the ABI wants and is needed to allow
* execution of PPC BSD programs.
*/
_regs->gpr[3] = tgetl(pos);
pos += sizeof(target_ulong);
_regs->gpr[4] = pos;
for (tmp = 1; tmp != 0; pos += sizeof(target_ulong))
tmp = ldl(pos);
_regs->gpr[5] = pos;
}
#define USE_ELF_CORE_DUMP
......@@ -263,8 +255,6 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info *
#endif
#define ELF_ARCH EM_MIPS
#define ELF_PLAT_INIT(_r)
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
regs->cp0_status = CP0St_UM;
......@@ -284,8 +274,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_SH
#define ELF_PLAT_INIT(_r) /* XXXXX */
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
/* Check other registers XXXXX */
......@@ -308,30 +296,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#include "elf.h"
/*
* 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 w/4KB pages!
*/
#define MAX_ARG_PAGES 32
/*
* This structure is used to hold the arguments that are
* used when loading binaries.
*/
struct linux_binprm {
char buf[128];
void *page[MAX_ARG_PAGES];
unsigned long p;
int sh_bang;
int fd;
int e_uid, e_gid;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
int dont_iput; /* binfmt handler has put inode */
};
struct exec
{
unsigned int a_info; /* Use macros N_MAGIC, etc for access */
......@@ -377,8 +341,6 @@ struct exec
#define PER_XENIX (0x0007 | STICKY_TIMEOUTS)
/* Necessary parameters */
#define NGROUPS 32
#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
......@@ -452,13 +414,13 @@ static void bswap_sym(Elf32_Sym *sym)
#endif
/*
* 'copy_string()' copies argument/envelope strings from user
* 'copy_elf_strings()' 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.
*
*/
static unsigned long copy_strings(int argc,char ** argv, void **page,
unsigned long p)
static unsigned long copy_elf_strings(int argc,char ** argv, void **page,
unsigned long p)
{
char *tmp, *tmp1, *pag = NULL;
int len, offset = 0;
......@@ -506,101 +468,6 @@ static unsigned long copy_strings(int argc,char ** argv, void **page,
return p;
}
static int in_group_p(gid_t g)
{
/* return TRUE if we're in the specified group, FALSE otherwise */
int ngroup;
int i;
gid_t grouplist[NGROUPS];
ngroup = getgroups(NGROUPS, grouplist);
for(i = 0; i < ngroup; i++) {
if(grouplist[i] == g) {
return 1;
}
}
return 0;
}
static int count(char ** vec)
{
int i;
for(i = 0; *vec; i++) {
vec++;
}
return(i);
}
static int prepare_binprm(struct linux_binprm *bprm)
{
struct stat st;
int mode;
int retval, id_change;
if(fstat(bprm->fd, &st) < 0) {
return(-errno);
}
mode = st.st_mode;
if(!S_ISREG(mode)) { /* Must be regular file */
return(-EACCES);
}
if(!(mode & 0111)) { /* Must have at least one execute bit set */
return(-EACCES);
}
bprm->e_uid = geteuid();
bprm->e_gid = getegid();
id_change = 0;
/* Set-uid? */
if(mode & S_ISUID) {
bprm->e_uid = st.st_uid;
if(bprm->e_uid != geteuid()) {
id_change = 1;
}
}
/* Set-gid? */
/*
* If setgid is set but no group execute bit then this
* is a candidate for mandatory locking, not a setgid
* executable.
*/
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
bprm->e_gid = st.st_gid;
if (!in_group_p(bprm->e_gid)) {
id_change = 1;
}
}
memset(bprm->buf, 0, sizeof(bprm->buf));
retval = lseek(bprm->fd, 0L, SEEK_SET);
if(retval >= 0) {
retval = read(bprm->fd, bprm->buf, 128);
}
if(retval < 0) {
perror("prepare_binprm");
exit(-1);
/* return(-errno); */
}
else {
return(retval);
}
}
static inline void memcpy_to_target(target_ulong dest, const void *src,
unsigned long len)
{
void *host_ptr;
host_ptr = lock_user(dest, len, 0);
memcpy(host_ptr, src, len);
unlock_user(host_ptr, dest, 1);
}
unsigned long setup_arg_pages(target_ulong p, struct linux_binprm * bprm,
struct image_info * info)
{
......@@ -628,11 +495,6 @@ unsigned long setup_arg_pages(target_ulong p, struct linux_binprm * bprm,
stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
p += stack_base;
if (bprm->loader) {
bprm->loader += stack_base;
}
bprm->exec += stack_base;
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
if (bprm->page[i]) {
info->rss++;
......@@ -703,7 +565,6 @@ static unsigned long create_elf_tables(target_ulong p, int argc, int envc,
unsigned long interp_load_addr, int ibcs,
struct image_info *info)
{
target_ulong argv, envp;
target_ulong sp;
int size;
target_ulong u_platform;
......@@ -765,28 +626,7 @@ static unsigned long create_elf_tables(target_ulong p, int argc, int envc,
#endif
#undef NEW_AUX_ENT
sp -= (envc + 1) * n;
envp = sp;
sp -= (argc + 1) * n;
argv = sp;
if (!ibcs) {
sp -= n; tputl(sp, envp);
sp -= n; tputl(sp, argv);
}
sp -= n; tputl(sp, argc);
info->arg_start = p;
while (argc-->0) {
tputl(argv, p); argv += n;
p += target_strlen(p) + 1;
}
tputl(argv, 0);
info->arg_end = info->env_start = p;
while (envc-->0) {
tputl(envp, p); envp += n;
p += target_strlen(p) + 1;
}
tputl(envp, 0);
info->env_end = p;
sp = loader_build_argptr(envc, argc, sp, p, !ibcs);
return sp;
}
......@@ -1001,8 +841,8 @@ static void load_symbols(struct elfhdr *hdr, int fd)
syminfos = s;
}
static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
struct image_info * info)
int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
struct image_info * info)
{
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
......@@ -1034,17 +874,19 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
bswap_ehdr(&elf_ex);
#endif
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) ||
(! elf_check_arch(elf_ex.e_machine))) {
return -ENOEXEC;
}
bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
if (!bprm->p) {
retval = -E2BIG;
}
/* Now read in all of the header information */
elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum);
if (elf_phdata == NULL) {
......@@ -1188,7 +1030,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
/* OK, we are done with that, now set up the arg stuff,
and then start this sucker up */
if (!bprm->sh_bang) {
{
char * passed_p;
if (interpreter_type == INTERPRETER_AOUT) {
......@@ -1196,7 +1038,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
passed_p = passed_fileno;
if (elf_interpreter) {
bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p);
bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p);
bprm->argc++;
}
}
......@@ -1347,11 +1189,10 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1),
info);
if (interpreter_type == INTERPRETER_AOUT)
info->arg_start += strlen(passed_fileno) + 1;
info->start_brk = info->brk = elf_brk;
info->end_code = end_code;
info->start_code = start_code;
info->start_data = end_code;
info->end_data = end_data;
info->start_stack = bprm->p;
......@@ -1380,79 +1221,18 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
MAP_FIXED | MAP_PRIVATE, -1, 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.
*/
ELF_PLAT_INIT(regs);
#endif
info->entry = elf_entry;
return 0;
}
int elf_exec(const char * filename, char ** argv, char ** envp,
struct target_pt_regs * regs, struct image_info *infop)
{
struct linux_binprm bprm;
int retval;
int i;
bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
bprm.page[i] = 0;
retval = open(filename, O_RDONLY);
if (retval < 0)
return retval;
bprm.fd = retval;
bprm.filename = (char *)filename;
bprm.sh_bang = 0;
bprm.loader = 0;
bprm.exec = 0;
bprm.dont_iput = 0;
bprm.argc = count(argv);
bprm.envc = count(envp);
retval = prepare_binprm(&bprm);
if(retval>=0) {
bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p);
bprm.exec = bprm.p;
bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p);
bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p);
if (!bprm.p) {
retval = -E2BIG;
}
}
if(retval>=0) {
retval = load_elf_binary(&bprm,regs,infop);
}
if(retval>=0) {
/* success. Initialize important registers */
init_thread(regs, infop);
return retval;
}
/* Something went wrong, return the inode and free the argument pages*/
for (i=0 ; i<MAX_ARG_PAGES ; i++) {
free(bprm.page[i]);
}
return(retval);
}
static int load_aout_interp(void * exptr, int interp_fd)
{
printf("a.out interpreter not yet supported\n");
return(0);
}
void do_init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
init_thread(regs, infop);
}
/*
* Copyright (C) 2002-2003 David McCullough <davidm@snapgear.com>
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
* The Silver Hammer Group, Ltd.
*
* This file provides the definitions and structures needed to
* support uClinux flat-format executables.
*/
#define FLAT_VERSION 0x00000004L
#ifdef CONFIG_BINFMT_SHARED_FLAT
#define MAX_SHARED_LIBS (4)
#else
#define MAX_SHARED_LIBS (1)
#endif
/*
* To make everything easier to port and manage cross platform
* development, all fields are in network byte order.
*/
struct flat_hdr {
char magic[4];
unsigned long rev; /* version (as above) */
unsigned long entry; /* Offset of first executable instruction
with text segment from beginning of file */
unsigned long data_start; /* Offset of data segment from beginning of
file */
unsigned long data_end; /* Offset of end of data segment
from beginning of file */
unsigned long bss_end; /* Offset of end of bss segment from beginning
of file */
/* (It is assumed that data_end through bss_end forms the bss segment.) */
unsigned long stack_size; /* Size of stack, in bytes */
unsigned long reloc_start; /* Offset of relocation records from
beginning of file */
unsigned long reloc_count; /* Number of relocation records */
unsigned long flags;
unsigned long build_date; /* When the program/library was built */
unsigned long filler[5]; /* Reservered, set to zero */
};
#define FLAT_FLAG_RAM 0x0001 /* load program entirely into RAM */
#define FLAT_FLAG_GOTPIC 0x0002 /* program is PIC with GOT */
#define FLAT_FLAG_GZIP 0x0004 /* all but the header is compressed */
#define FLAT_FLAG_GZDATA 0x0008 /* only data/relocs are compressed (for XIP) */
#define FLAT_FLAG_KTRACE 0x0010 /* output useful kernel trace for debugging */
/*
* While it would be nice to keep this header clean, users of older
* tools still need this support in the kernel. So this section is
* purely for compatibility with old tool chains.
*
* DO NOT make changes or enhancements to the old format please, just work
* with the format above, except to fix bugs with old format support.
*/
#define OLD_FLAT_VERSION 0x00000002L
#define OLD_FLAT_RELOC_TYPE_TEXT 0
#define OLD_FLAT_RELOC_TYPE_DATA 1
#define OLD_FLAT_RELOC_TYPE_BSS 2
# define OLD_FLAT_FLAG_RAM 0x1 /* load program entirely into RAM */
This diff is collapsed.
/* Code for loading Linux executables. Mostly linux kenrel code. */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "qemu.h"
#define NGROUPS 32
/* ??? This should really be somewhere else. */
void memcpy_to_target(target_ulong dest, const void *src,
unsigned long len)
{
void *host_ptr;
host_ptr = lock_user(dest, len, 0);
memcpy(host_ptr, src, len);
unlock_user(host_ptr, dest, 1);
}
static int in_group_p(gid_t g)
{
/* return TRUE if we're in the specified group, FALSE otherwise */
int ngroup;
int i;
gid_t grouplist[NGROUPS];
ngroup = getgroups(NGROUPS, grouplist);
for(i = 0; i < ngroup; i++) {