URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [fs/] [binfmt_elf.c] - Rev 199
Go to most recent revision | Compare with Previous | Blame | View Log
/* * linux/fs/binfmt_elf.c * * These are the functions used to load ELF format executables as used * on SVr4 machines. Information on the format may be found in the book * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support * Tools". * * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). */ #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> #define STACK_SIZE (1*PAGE_SIZE) static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); #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, NULL, NULL #else NULL, &mod_use_count_, load_elf_binary, load_elf_library, elf_core_dump #endif }; struct sec_add { unsigned long pm_add; unsigned long vm_add; int len; } sec[ELF_SECTION_NB]; unsigned short *or32_consth_add = (unsigned short *)NULL; unsigned long or32_consth_rel; unsigned long *create_elf_tables(char *p, int argc, int envc, struct pt_regs *regs) { unsigned long *argv, *envp; unsigned long *sp; /* * Force 16 byte alignment here for generality. */ sp = (unsigned long *) (~15UL & (unsigned long) p); sp -= 2; sp -= envc + 1; envp = sp; sp -= argc + 1; argv = sp; regs->gprs[1] = argc; put_user((unsigned long) argc, --sp); current->mm->arg_start = (unsigned long) p; regs->gprs[2] = (unsigned long) argv; while (argc-- > 0) { put_user(p, argv++); while (get_user(p++)) /* nothing */ ; } put_user(0, argv); current->mm->arg_end = current->mm->env_start = (unsigned long) p; regs->gprs[3] = (unsigned long) envp; while (envc-- > 0) { put_user(p, envp++); while (get_user(p++)) /* nothing */ ; } put_user(0, envp); current->mm->env_end = (unsigned long) p; return sp; } static unsigned long putstring(unsigned long p, char * string) { unsigned long l = strlen(string)+1; p -= l; memcpy((void*)p, string, l); return p; } static unsigned long putstringarray(unsigned long p, int count, char ** array) { while(count) { p=putstring(p, array[--count]); } return p; } static unsigned long stringarraylen(int count, char ** array) { int l = 4; while(count) { l += strlen(array[--count]); l++; l+=4; } return l; } int do_relocate(int dst_indx, int rel_nb, struct elf32_rel *rel_ptr, struct elf32_sym *sym_ptr, char *str_ptr, struct sec_add *sec) { struct elf32_sym *sym_tab; void *rel_loc; unsigned long tmp; int src_indx; int i; for(i = 0; i < rel_nb; i++, rel_ptr++) { /* Symbol tab */ sym_tab = sym_ptr + (rel_ptr->r_info >> 8); /* Section index of section that contains symbol */ src_indx = sym_tab->st_shndx; /* Location in physical memory to which this relocation is refering to */ rel_loc = (void *)(rel_ptr->r_offset + sec[dst_indx].pm_add); if((rel_ptr->r_info & 0x000000ff) == R_OR32_32) { *(unsigned long *)rel_loc = *(unsigned long *)rel_loc /* + sym_tab->st_value*/ - sec[src_indx].vm_add + sec[src_indx].pm_add; } else if((rel_ptr->r_info & 0x000000ff) == R_OR32_16) { *(unsigned short *)rel_loc = *(unsigned short *)rel_loc /* + sym_tab->st_value*/ - sec[src_indx].vm_add + sec[src_indx].pm_add; } else if((rel_ptr->r_info & 0x000000ff) == R_OR32_8) { *(unsigned char *)rel_loc = *(unsigned char *)rel_loc /* + sym_tab->st_value*/ - sec[src_indx].vm_add + sec[src_indx].pm_add; } else if((rel_ptr->r_info & 0x000000ff) == R_OR32_CONSTH) { or32_consth_add = (((unsigned short *)rel_loc) + 1); or32_consth_rel = *or32_consth_add << 16; } else if((rel_ptr->r_info & 0x000000ff) == R_OR32_CONST) { tmp = or32_consth_rel | *(((unsigned short *)rel_loc) + 1); tmp = tmp/* + sym_tab->st_value*/ - sec[src_indx].vm_add + sec[src_indx].pm_add; *(((unsigned short *)rel_loc) + 1) = tmp & 0x0000ffff; if(or32_consth_add != (unsigned short *)NULL) { *or32_consth_add = tmp >> 16; or32_consth_add = (unsigned short *)NULL; or32_consth_rel = 0; } } else if((rel_ptr->r_info & 0x000000ff) == R_OR32_JUMPTARG) { tmp = ((*(unsigned long *)rel_loc) & 0x03ffffff); tmp = (tmp & 0x02000000) ? (tmp | 0xfc000000) : tmp; tmp = tmp + ((sym_tab->st_value - sec[src_indx].vm_add + sec[src_indx].pm_add - sec[dst_indx].pm_add) >> 2); *(unsigned long *)rel_loc = ((*(unsigned long *)rel_loc) & 0xfc000000) | (tmp & 0x03ffffff); } else { return -ELNRNG; } } return 0; } unsigned long do_find_main(int sym_nb, struct elf32_sym *sym_prt, char *str_ptr) { int i; for(i = 0; i < sym_nb; i++, sym_prt++) { if(sym_prt->st_name == 0) continue; if(strcmp(&str_ptr[sym_prt->st_name], "_main") == 0) return (sym_prt->st_value + sec[sym_prt->st_shndx].pm_add); } return 0; } /* * 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 file *file; unsigned char ibcs2_interpreter; int i,j; int old_fs; struct elf32_shdr *elf_spnt, *elf_shdata; int elf_exec_fileno; unsigned long elf_entry = 0; int status; unsigned long start_code, end_code, start_data, end_data, start_brk, brk; unsigned long elf_stack; int rel_indx, symtab_indx, strtab_indx; struct elf32_rel *rel_ptr; struct elf32_sym *sym_ptr; char *str_ptr; int retval; printk("%s - %s:%d\n",__FILE__,__FUNCTION__,__LINE__); printk(" starting sp = %x\n", regs); or32_consth_add = 0; or32_consth_rel = 0; ibcs2_interpreter = 0; status = 0; elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ 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_REL || (!elf_check_arch(elf_ex.e_machine)) || (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || !bprm->inode->i_op->default_file_ops->mmap)) { return -ENOEXEC; } if(elf_ex.e_shnum > ELF_SECTION_NB) return -ETOOMANYSECT; for(i = 0; i < ELF_SECTION_NB; i++) sec[i].len = 0; /* Now read in all of the header information */ elf_shdata = (struct elf32_shdr *) kmalloc(elf_ex.e_shnum * elf_ex.e_shentsize, GFP_KERNEL); if (elf_shdata == NULL) { return -ENOMEM; } retval = read_exec(bprm->inode, elf_ex.e_shoff, (char *) elf_shdata, elf_ex.e_shentsize * elf_ex.e_shnum, 1); if (retval < 0) { kfree(elf_shdata); return retval; } elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); if (elf_exec_fileno < 0) { kfree(elf_shdata); return elf_exec_fileno; } file = current->files->fd[elf_exec_fileno]; elf_stack = ~0UL; start_code = ~0UL; end_code = 0; start_data = ~0UL; end_data = 0; start_brk = ~0UL; brk = 0; if (flush_old_exec(bprm)) { return -ENOMEM; } /* OK, This is the point of no return */ current->mm->end_data = 0; current->mm->end_code = 0; /* Now we do a little grungy work by mmaping the ELF image into the memory. At this point, we assume that at a variable address. */ old_fs = get_fs(); set_fs(get_ds()); for(i = 0, elf_spnt = elf_shdata; i < elf_ex.e_shnum; i++, elf_spnt++) { if((elf_spnt->sh_flags & SHF_ALLOC)) { int elf_prot = PROT_READ; unsigned long retval; if (elf_spnt->sh_flags & SHF_WRITE) elf_prot |= PROT_WRITE; if (elf_spnt->sh_flags & SHF_EXECINSTR) elf_prot |= PROT_EXEC; if(elf_spnt->sh_size == 0) continue; retval = (unsigned long)do_mmap(NULL, 0, elf_spnt->sh_size, elf_prot, 0, 0); if(retval > (unsigned long)-4096) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } sec[i].pm_add = retval; sec[i].vm_add = elf_spnt->sh_addr; sec[i].len = elf_spnt->sh_size; if(elf_spnt->sh_type == SHT_PROGBITS) { retval = read_exec(bprm->inode, elf_spnt->sh_offset, (char *)retval, elf_spnt->sh_size, 1); if (retval < 0) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } } if(elf_spnt->sh_type == SHT_PROGBITS) { if(elf_spnt->sh_flags & SHF_EXECINSTR) { if(sec[i].pm_add < start_code) start_code = sec[i].pm_add; if((sec[i].pm_add + elf_spnt->sh_size) > end_code) end_code = sec[i].pm_add + elf_spnt->sh_size; } else { if(sec[i].pm_add < start_data) start_data = sec[i].pm_add; if((sec[i].pm_add + elf_spnt->sh_size) > end_data) end_data = sec[i].pm_add + elf_spnt->sh_size; } } else if(elf_spnt->sh_type == SHT_NOBITS) { if(sec[i].pm_add < start_brk) start_brk = sec[i].pm_add; if((sec[i].pm_add + elf_spnt->sh_size) > brk) brk = sec[i].pm_add + elf_spnt->sh_size; } } } for(i = 0, elf_spnt = elf_shdata; i < elf_ex.e_shnum; elf_spnt++, i++) { if(elf_spnt->sh_type == SHT_SYMTAB && (elf_spnt->sh_size != 0)) { struct elf32_shdr *link_shdr; int sym_nb; unsigned long retval; /* Map symtab section */ retval = (unsigned long)do_mmap(NULL, 0, elf_spnt->sh_size, PROT_READ, 0, 0); if(retval > (unsigned long)-4096) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } sec[symtab_indx].pm_add = retval; sym_ptr = (struct elf32_sym *)retval; sec[symtab_indx].len = elf_spnt->sh_size; retval = read_exec(bprm->inode, elf_spnt->sh_offset, (char *)retval, elf_spnt->sh_size, 1); if (retval < 0) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } strtab_indx = elf_spnt->sh_link; link_shdr = elf_shdata + strtab_indx; /* Map strtab section */ retval = (unsigned long)do_mmap(NULL, 0, link_shdr->sh_size, PROT_READ, 0, 0); if(retval > (unsigned long)-4096) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } sec[strtab_indx].pm_add = retval; str_ptr = (char *)retval; sec[strtab_indx].len = link_shdr->sh_size; retval = read_exec(bprm->inode, link_shdr->sh_offset, (char *)retval, link_shdr->sh_size, 1); if (retval < 0) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } sym_nb = sec[symtab_indx].len / sizeof(struct elf32_sym); elf_entry = do_find_main(sym_nb, sym_ptr, str_ptr); break; } } for(i = 0, elf_spnt = elf_shdata; i < elf_ex.e_shnum; elf_spnt++, i++) { if(elf_spnt->sh_type == SHT_REL && (elf_spnt->sh_size != 0)) { struct elf32_shdr *link_shdr; int rel_nb; unsigned long retval; /* Map rel section */ retval = (unsigned long)do_mmap(NULL, 0, elf_spnt->sh_size, PROT_READ, 0, 0); if(retval > (unsigned long)-4096) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } sec[i].pm_add = retval; rel_ptr = (struct elf32_rel *)retval; sec[i].len = elf_spnt->sh_size; retval = read_exec(bprm->inode, elf_spnt->sh_offset, (char *)retval, elf_spnt->sh_size, 1); if (retval < 0) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } rel_indx = i; symtab_indx = elf_spnt->sh_link; link_shdr = elf_shdata + symtab_indx; /* Now do relocations for the n-th section. n is read from real setiona hader info field. */ rel_nb = sec[rel_indx].len / sizeof(struct elf32_rel); retval = do_relocate(elf_spnt->sh_info, rel_nb, rel_ptr, sym_ptr, str_ptr, sec); if (retval < 0) { for(j = 0; j < elf_ex.e_shnum; j++) if(sec[j].len) do_munmap(sec[j].pm_add, sec[j].len); kfree(elf_shdata); return retval; } /* Now unmap rel section */ do_munmap(sec[rel_indx].pm_add, sec[rel_indx].len); sec[rel_indx].len = 0; } } /* Now unmap sym and str sections */ do_munmap(sec[symtab_indx].pm_add, sec[symtab_indx].len); sec[symtab_indx].len = 0; do_munmap(sec[strtab_indx].pm_add, sec[strtab_indx].len); sec[strtab_indx].len = 0; set_fs(old_fs); kfree(elf_shdata); sys_close(elf_exec_fileno); current->personality = PER_LINUX; if (current->exec_domain && current->exec_domain->use_count) (*current->exec_domain->use_count)--; if (current->binfmt && current->binfmt->use_count) (*current->binfmt->use_count)--; current->exec_domain = lookup_exec_domain(current->personality); current->binfmt = &elf_format; if (current->exec_domain && current->exec_domain->use_count) (*current->exec_domain->use_count)++; if (current->binfmt && current->binfmt->use_count) (*current->binfmt->use_count)++; bprm->p = do_mmap(0, 0, STACK_SIZE, PROT_READ|PROT_WRITE, 0, 0) + STACK_SIZE; bprm->p = putstringarray(bprm->p, 1, &bprm->filename); bprm->p = putstringarray(bprm->p, bprm->envc, bprm->envp); bprm->p = putstringarray(bprm->p, bprm->argc, bprm->argv); 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)create_elf_tables((char *) bprm->p, bprm->argc, bprm->envc, regs); current->mm->start_brk = start_brk; current->mm->brk = brk; current->mm->start_code = start_code; current->mm->end_code = end_code; current->mm->start_data = start_data; current->mm->end_data = end_data; current->mm->start_stack = bprm->p; /* printk("%s - %s:%d\n",__FILE__,__FUNCTION__,__LINE__); printk(" start_code = %x\n", current->mm->start_code); printk(" end_code = %x\n", current->mm->end_code); printk(" start_data = %x\n", current->mm->start_data); printk(" end_data = %x\n", current->mm->end_data); printk(" start_brk = %x\n", current->mm->start_brk); printk(" brk = %x\n", current->mm->brk); printk(" start_stack = %x\n", current->mm->start_stack); printk(" arg_start = %x\n", current->mm->arg_start); printk(" env_start = %x\n", current->mm->env_start); printk(" env_end = %x\n", current->mm->env_end); printk(" elf_entry = %x\n", elf_entry); printk(".text\n"); for(i = 0; i < (end_code - start_code); i++) { if(!(i % 0x10)) printk("\n%.8x:", current->mm->start_code + i); printk(" %.2x", *(volatile unsigned char *)(current->mm->start_code + i)); } printk("\n"); printk(".data\n"); if(start_data != ~0UL) { for(i = 0; i < (end_data - start_data); i++) { if(!(i % 0x10)) printk("\n%.8x:", current->mm->start_data + i); printk(" %.2x", *(volatile unsigned char *)(current->mm->start_data + i)); } } printk("\n"); */ if(start_brk != ~0UL) { int nbyte = brk - start_brk; char *fpnt = (char *)brk; do { put_user(0, fpnt++); } while (--nbyte); } start_thread(regs, elf_entry, bprm->p); 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; } 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
Go to most recent revision | Compare with Previous | Blame | View Log