📄 binfmt_elf.c
字号:
/* * 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/file.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/shm.h>#include <linux/personality.h>#include <linux/elfcore.h>#include <linux/init.h>#include <linux/highuid.h>#include <linux/smp_lock.h>#include <linux/compiler.h>#include <linux/highmem.h>#include <asm/uaccess.h>#include <asm/param.h>#include <asm/pgalloc.h>#define DLINFO_ITEMS 13#include <linux/elf.h>static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs);static int load_elf_library(struct file*);static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int);extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);extern void dump_thread(struct pt_regs *, struct user *);#ifndef elf_addr_t#define elf_addr_t unsigned long#define elf_caddr_t char *#endif/* * If we don't support core dumping, then supply a NULL so we * don't even try. */#ifdef USE_ELF_CORE_DUMPstatic int elf_core_dump(long signr, struct pt_regs * regs, struct file * file);#else#define elf_core_dump NULL#endif#if ELF_EXEC_PAGESIZE > PAGE_SIZE# define ELF_MIN_ALIGN ELF_EXEC_PAGESIZE#else# define ELF_MIN_ALIGN PAGE_SIZE#endif#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))static struct linux_binfmt elf_format = { NULL, THIS_MODULE, load_elf_binary, load_elf_library, elf_core_dump, ELF_EXEC_PAGESIZE};#define BAD_ADDR(x) ((unsigned long)(x) > TASK_SIZE)static void set_brk(unsigned long start, unsigned long end){ start = ELF_PAGEALIGN(start); end = ELF_PAGEALIGN(end); if (end <= start) return; do_brk(start, end - start);}/* 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 */static void padzero(unsigned long elf_bss){ unsigned long nbyte; nbyte = ELF_PAGEOFFSET(elf_bss); if (nbyte) { nbyte = ELF_MIN_ALIGN - nbyte; clear_user((void *) elf_bss, nbyte); }}static elf_addr_t * create_elf_tables(char *p, int argc, int envc, struct elfhdr * exec, unsigned long load_addr, unsigned long load_bias, unsigned long interp_load_addr, int ibcs){ elf_caddr_t *argv; elf_caddr_t *envp; elf_addr_t *sp, *csp; char *k_platform, *u_platform; long hwcap; size_t platform_len = 0; size_t len; /* * Get hold of platform and hardware capabilities masks for * the machine we are running on. In some cases (Sparc), * this info is impossible to get, in others (i386) it is * merely difficult. */ hwcap = ELF_HWCAP; k_platform = ELF_PLATFORM; if (k_platform) { platform_len = strlen(k_platform) + 1; u_platform = p - platform_len; __copy_to_user(u_platform, k_platform, platform_len); } else u_platform = p;#if defined(__i386__) && defined(CONFIG_SMP) /* * In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions * by the processes running on the same package. One thing we can do * is to shuffle the initial stack for them. * * The conditionals here are unneeded, but kept in to make the * code behaviour the same as pre change unless we have hyperthreaded * processors. This keeps Mr Marcelo Person happier but should be * removed for 2.5 */ if(smp_num_siblings > 1) u_platform = u_platform - ((current->pid % 64) << 7);#endif /* * Force 16 byte _final_ alignment here for generality. */ sp = (elf_addr_t *)(~15UL & (unsigned long)(u_platform)); csp = sp; csp -= (1+DLINFO_ITEMS)*2 + (k_platform ? 2 : 0);#ifdef DLINFO_ARCH_ITEMS csp -= DLINFO_ARCH_ITEMS*2;#endif csp -= envc+1; csp -= argc+1; csp -= (!ibcs ? 3 : 1); /* argc itself */ if ((unsigned long)csp & 15UL) sp -= ((unsigned long)csp & 15UL) / sizeof(*sp); /* * Put the ELF interpreter info on the stack */#define NEW_AUX_ENT(nr, id, val) \ __put_user ((id), sp+(nr*2)); \ __put_user ((val), sp+(nr*2+1)); \ sp -= 2; NEW_AUX_ENT(0, AT_NULL, 0); if (k_platform) { sp -= 2; NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform); } sp -= DLINFO_ITEMS*2; NEW_AUX_ENT( 0, AT_HWCAP, hwcap); NEW_AUX_ENT( 1, AT_PAGESZ, ELF_EXEC_PAGESIZE); NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC); NEW_AUX_ENT( 3, AT_PHDR, load_addr + exec->e_phoff); NEW_AUX_ENT( 4, AT_PHENT, sizeof (struct elf_phdr)); NEW_AUX_ENT( 5, AT_PHNUM, exec->e_phnum); NEW_AUX_ENT( 6, AT_BASE, interp_load_addr); NEW_AUX_ENT( 7, AT_FLAGS, 0); NEW_AUX_ENT( 8, AT_ENTRY, load_bias + exec->e_entry); NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid); NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid); NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid); NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);#ifdef ARCH_DLINFO /* * ARCH_DLINFO must come last so platform specific code can enforce * special alignment requirements on the AUXV if necessary (eg. PPC). */ ARCH_DLINFO;#endif#undef NEW_AUX_ENT sp -= envc+1; envp = (elf_caddr_t *) sp; sp -= argc+1; argv = (elf_caddr_t *) sp; if (!ibcs) { __put_user((elf_addr_t)(unsigned long) envp,--sp); __put_user((elf_addr_t)(unsigned long) argv,--sp); } __put_user((elf_addr_t)argc,--sp); current->mm->arg_start = (unsigned long) p; while (argc-->0) { __put_user((elf_caddr_t)(unsigned long)p,argv++); len = strnlen_user(p, PAGE_SIZE*MAX_ARG_PAGES); if (!len || len > PAGE_SIZE*MAX_ARG_PAGES) return NULL; p += len; } __put_user(NULL, argv); current->mm->arg_end = current->mm->env_start = (unsigned long) p; while (envc-->0) { __put_user((elf_caddr_t)(unsigned long)p,envp++); len = strnlen_user(p, PAGE_SIZE*MAX_ARG_PAGES); if (!len || len > PAGE_SIZE*MAX_ARG_PAGES) return NULL; p += len; } __put_user(NULL, envp); current->mm->env_end = (unsigned long) p; return sp;}#ifndef elf_mapstatic inline unsigned longelf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type){ unsigned long map_addr; down_write(¤t->mm->mmap_sem); map_addr = do_mmap(filep, ELF_PAGESTART(addr), eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type, eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); up_write(¤t->mm->mmap_sem); return(map_addr);}#endif /* !elf_map *//* 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 */static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, struct file * interpreter, unsigned long *interp_load_addr){ struct elf_phdr *elf_phdata; struct elf_phdr *eppnt; unsigned long load_addr = 0; int load_addr_set = 0; unsigned long last_bss = 0, elf_bss = 0; unsigned long error = ~0UL; int retval, i, size; /* First of all, some simple consistency checks */ if (interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) goto out; if (!elf_check_arch(interp_elf_ex)) goto out; if (!interpreter->f_op || !interpreter->f_op->mmap) goto out; /* * If the size of this structure has changed, then punt, since * we will be doing the wrong thing. */ if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) goto out; if (interp_elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr)) goto out; /* Now read in all of the header information */ size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum; if (size > ELF_MIN_ALIGN) goto out; elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); if (!elf_phdata) goto out; retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size); error = retval; if (retval < 0) goto out_close; eppnt = elf_phdata; for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) { if (eppnt->p_type == PT_LOAD) { int elf_type = MAP_PRIVATE | MAP_DENYWRITE; int elf_prot = 0; unsigned long vaddr = 0; unsigned long k, map_addr; 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; vaddr = eppnt->p_vaddr; if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) elf_type |= MAP_FIXED; map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type); if (BAD_ADDR(map_addr)) goto out_close; if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { load_addr = map_addr - ELF_PAGESTART(vaddr); 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. */ 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. */ /* * 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 zero-mapped pages up to and including the * last bss page. */ padzero(elf_bss); elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far */ /* Map the last of the bss segment */ if (last_bss > elf_bss) do_brk(elf_bss, last_bss - elf_bss); *interp_load_addr = load_addr; error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;out_close: kfree(elf_phdata);out: return error;}static unsigned long load_aout_interp(struct exec * interp_ex, struct file * interpreter){ unsigned long text_data, elf_entry = ~0UL; char * addr; loff_t offset; int retval; current->mm->end_code = interp_ex->a_text; text_data = interp_ex->a_text + interp_ex->a_data; current->mm->end_data = text_data; current->mm->brk = interp_ex->a_bss + text_data; switch (N_MAGIC(*interp_ex)) { case OMAGIC: offset = 32; addr = (char *) 0; break; case ZMAGIC: case QMAGIC: offset = N_TXTOFF(*interp_ex); addr = (char *) N_TXTADDR(*interp_ex); break; default: goto out; } do_brk(0, text_data); retval = -ENOEXEC; if (!interpreter->f_op || !interpreter->f_op->read) goto out; retval = interpreter->f_op->read(interpreter, addr, text_data, &offset); if (retval < 0) goto out; flush_icache_range((unsigned long)addr, (unsigned long)addr + text_data); do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1), interp_ex->a_bss); elf_entry = interp_ex->a_entry;out: 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 2static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -