162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* binfmt_elf_fdpic.c: FDPIC ELF binary format 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci * Derived from binfmt_elf.c 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/stat.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/sched/coredump.h> 1562306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1662306a36Sopenharmony_ci#include <linux/sched/cputime.h> 1762306a36Sopenharmony_ci#include <linux/mm.h> 1862306a36Sopenharmony_ci#include <linux/mman.h> 1962306a36Sopenharmony_ci#include <linux/errno.h> 2062306a36Sopenharmony_ci#include <linux/signal.h> 2162306a36Sopenharmony_ci#include <linux/binfmts.h> 2262306a36Sopenharmony_ci#include <linux/string.h> 2362306a36Sopenharmony_ci#include <linux/file.h> 2462306a36Sopenharmony_ci#include <linux/fcntl.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/pagemap.h> 2762306a36Sopenharmony_ci#include <linux/security.h> 2862306a36Sopenharmony_ci#include <linux/highmem.h> 2962306a36Sopenharmony_ci#include <linux/highuid.h> 3062306a36Sopenharmony_ci#include <linux/personality.h> 3162306a36Sopenharmony_ci#include <linux/ptrace.h> 3262306a36Sopenharmony_ci#include <linux/init.h> 3362306a36Sopenharmony_ci#include <linux/elf.h> 3462306a36Sopenharmony_ci#include <linux/elf-fdpic.h> 3562306a36Sopenharmony_ci#include <linux/elfcore.h> 3662306a36Sopenharmony_ci#include <linux/coredump.h> 3762306a36Sopenharmony_ci#include <linux/dax.h> 3862306a36Sopenharmony_ci#include <linux/regset.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <linux/uaccess.h> 4162306a36Sopenharmony_ci#include <asm/param.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_citypedef char *elf_caddr_t; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#if 0 4662306a36Sopenharmony_ci#define kdebug(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ ) 4762306a36Sopenharmony_ci#else 4862306a36Sopenharmony_ci#define kdebug(fmt, ...) do {} while(0) 4962306a36Sopenharmony_ci#endif 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#if 0 5262306a36Sopenharmony_ci#define kdcore(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ ) 5362306a36Sopenharmony_ci#else 5462306a36Sopenharmony_ci#define kdcore(fmt, ...) do {} while(0) 5562306a36Sopenharmony_ci#endif 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int load_elf_fdpic_binary(struct linux_binprm *); 6062306a36Sopenharmony_cistatic int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *, struct file *); 6162306a36Sopenharmony_cistatic int elf_fdpic_map_file(struct elf_fdpic_params *, struct file *, 6262306a36Sopenharmony_ci struct mm_struct *, const char *); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int create_elf_fdpic_tables(struct linux_binprm *, struct mm_struct *, 6562306a36Sopenharmony_ci struct elf_fdpic_params *, 6662306a36Sopenharmony_ci struct elf_fdpic_params *); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#ifndef CONFIG_MMU 6962306a36Sopenharmony_cistatic int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *, 7062306a36Sopenharmony_ci struct file *, 7162306a36Sopenharmony_ci struct mm_struct *); 7262306a36Sopenharmony_ci#endif 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *, 7562306a36Sopenharmony_ci struct file *, struct mm_struct *); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#ifdef CONFIG_ELF_CORE 7862306a36Sopenharmony_cistatic int elf_fdpic_core_dump(struct coredump_params *cprm); 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct linux_binfmt elf_fdpic_format = { 8262306a36Sopenharmony_ci .module = THIS_MODULE, 8362306a36Sopenharmony_ci .load_binary = load_elf_fdpic_binary, 8462306a36Sopenharmony_ci#ifdef CONFIG_ELF_CORE 8562306a36Sopenharmony_ci .core_dump = elf_fdpic_core_dump, 8662306a36Sopenharmony_ci .min_coredump = ELF_EXEC_PAGESIZE, 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int __init init_elf_fdpic_binfmt(void) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci register_binfmt(&elf_fdpic_format); 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void __exit exit_elf_fdpic_binfmt(void) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci unregister_binfmt(&elf_fdpic_format); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cicore_initcall(init_elf_fdpic_binfmt); 10262306a36Sopenharmony_cimodule_exit(exit_elf_fdpic_binfmt); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int is_elf(struct elfhdr *hdr, struct file *file) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0) 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci if (!elf_check_arch(hdr)) 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci if (!file->f_op->mmap) 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci return 1; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#ifndef elf_check_fdpic 11862306a36Sopenharmony_ci#define elf_check_fdpic(x) 0 11962306a36Sopenharmony_ci#endif 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#ifndef elf_check_const_displacement 12262306a36Sopenharmony_ci#define elf_check_const_displacement(x) 0 12362306a36Sopenharmony_ci#endif 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int is_constdisp(struct elfhdr *hdr) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci if (!elf_check_fdpic(hdr)) 12862306a36Sopenharmony_ci return 1; 12962306a36Sopenharmony_ci if (elf_check_const_displacement(hdr)) 13062306a36Sopenharmony_ci return 1; 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/*****************************************************************************/ 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * read the program headers table into memory 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, 13962306a36Sopenharmony_ci struct file *file) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct elf_phdr *phdr; 14262306a36Sopenharmony_ci unsigned long size; 14362306a36Sopenharmony_ci int retval, loop; 14462306a36Sopenharmony_ci loff_t pos = params->hdr.e_phoff; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (params->hdr.e_phentsize != sizeof(struct elf_phdr)) 14762306a36Sopenharmony_ci return -ENOMEM; 14862306a36Sopenharmony_ci if (params->hdr.e_phnum > 65536U / sizeof(struct elf_phdr)) 14962306a36Sopenharmony_ci return -ENOMEM; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci size = params->hdr.e_phnum * sizeof(struct elf_phdr); 15262306a36Sopenharmony_ci params->phdrs = kmalloc(size, GFP_KERNEL); 15362306a36Sopenharmony_ci if (!params->phdrs) 15462306a36Sopenharmony_ci return -ENOMEM; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci retval = kernel_read(file, params->phdrs, size, &pos); 15762306a36Sopenharmony_ci if (unlikely(retval != size)) 15862306a36Sopenharmony_ci return retval < 0 ? retval : -ENOEXEC; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* determine stack size for this binary */ 16162306a36Sopenharmony_ci phdr = params->phdrs; 16262306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { 16362306a36Sopenharmony_ci if (phdr->p_type != PT_GNU_STACK) 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (phdr->p_flags & PF_X) 16762306a36Sopenharmony_ci params->flags |= ELF_FDPIC_FLAG_EXEC_STACK; 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci params->flags |= ELF_FDPIC_FLAG_NOEXEC_STACK; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci params->stack_size = phdr->p_memsz; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/*****************************************************************************/ 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * load an fdpic binary into various bits of memory 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_cistatic int load_elf_fdpic_binary(struct linux_binprm *bprm) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct elf_fdpic_params exec_params, interp_params; 18562306a36Sopenharmony_ci struct pt_regs *regs = current_pt_regs(); 18662306a36Sopenharmony_ci struct elf_phdr *phdr; 18762306a36Sopenharmony_ci unsigned long stack_size, entryaddr; 18862306a36Sopenharmony_ci#ifdef ELF_FDPIC_PLAT_INIT 18962306a36Sopenharmony_ci unsigned long dynaddr; 19062306a36Sopenharmony_ci#endif 19162306a36Sopenharmony_ci#ifndef CONFIG_MMU 19262306a36Sopenharmony_ci unsigned long stack_prot; 19362306a36Sopenharmony_ci#endif 19462306a36Sopenharmony_ci struct file *interpreter = NULL; /* to shut gcc up */ 19562306a36Sopenharmony_ci char *interpreter_name = NULL; 19662306a36Sopenharmony_ci int executable_stack; 19762306a36Sopenharmony_ci int retval, i; 19862306a36Sopenharmony_ci loff_t pos; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci kdebug("____ LOAD %d ____", current->pid); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci memset(&exec_params, 0, sizeof(exec_params)); 20362306a36Sopenharmony_ci memset(&interp_params, 0, sizeof(interp_params)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci exec_params.hdr = *(struct elfhdr *) bprm->buf; 20662306a36Sopenharmony_ci exec_params.flags = ELF_FDPIC_FLAG_PRESENT | ELF_FDPIC_FLAG_EXECUTABLE; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* check that this is a binary we know how to deal with */ 20962306a36Sopenharmony_ci retval = -ENOEXEC; 21062306a36Sopenharmony_ci if (!is_elf(&exec_params.hdr, bprm->file)) 21162306a36Sopenharmony_ci goto error; 21262306a36Sopenharmony_ci if (!elf_check_fdpic(&exec_params.hdr)) { 21362306a36Sopenharmony_ci#ifdef CONFIG_MMU 21462306a36Sopenharmony_ci /* binfmt_elf handles non-fdpic elf except on nommu */ 21562306a36Sopenharmony_ci goto error; 21662306a36Sopenharmony_ci#else 21762306a36Sopenharmony_ci /* nommu can only load ET_DYN (PIE) ELF */ 21862306a36Sopenharmony_ci if (exec_params.hdr.e_type != ET_DYN) 21962306a36Sopenharmony_ci goto error; 22062306a36Sopenharmony_ci#endif 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* read the program header table */ 22462306a36Sopenharmony_ci retval = elf_fdpic_fetch_phdrs(&exec_params, bprm->file); 22562306a36Sopenharmony_ci if (retval < 0) 22662306a36Sopenharmony_ci goto error; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* scan for a program header that specifies an interpreter */ 22962306a36Sopenharmony_ci phdr = exec_params.phdrs; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) { 23262306a36Sopenharmony_ci switch (phdr->p_type) { 23362306a36Sopenharmony_ci case PT_INTERP: 23462306a36Sopenharmony_ci retval = -ENOMEM; 23562306a36Sopenharmony_ci if (phdr->p_filesz > PATH_MAX) 23662306a36Sopenharmony_ci goto error; 23762306a36Sopenharmony_ci retval = -ENOENT; 23862306a36Sopenharmony_ci if (phdr->p_filesz < 2) 23962306a36Sopenharmony_ci goto error; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* read the name of the interpreter into memory */ 24262306a36Sopenharmony_ci interpreter_name = kmalloc(phdr->p_filesz, GFP_KERNEL); 24362306a36Sopenharmony_ci if (!interpreter_name) 24462306a36Sopenharmony_ci goto error; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci pos = phdr->p_offset; 24762306a36Sopenharmony_ci retval = kernel_read(bprm->file, interpreter_name, 24862306a36Sopenharmony_ci phdr->p_filesz, &pos); 24962306a36Sopenharmony_ci if (unlikely(retval != phdr->p_filesz)) { 25062306a36Sopenharmony_ci if (retval >= 0) 25162306a36Sopenharmony_ci retval = -ENOEXEC; 25262306a36Sopenharmony_ci goto error; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci retval = -ENOENT; 25662306a36Sopenharmony_ci if (interpreter_name[phdr->p_filesz - 1] != '\0') 25762306a36Sopenharmony_ci goto error; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci kdebug("Using ELF interpreter %s", interpreter_name); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* replace the program with the interpreter */ 26262306a36Sopenharmony_ci interpreter = open_exec(interpreter_name); 26362306a36Sopenharmony_ci retval = PTR_ERR(interpreter); 26462306a36Sopenharmony_ci if (IS_ERR(interpreter)) { 26562306a36Sopenharmony_ci interpreter = NULL; 26662306a36Sopenharmony_ci goto error; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * If the binary is not readable then enforce 27162306a36Sopenharmony_ci * mm->dumpable = 0 regardless of the interpreter's 27262306a36Sopenharmony_ci * permissions. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci would_dump(bprm, interpreter); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci pos = 0; 27762306a36Sopenharmony_ci retval = kernel_read(interpreter, bprm->buf, 27862306a36Sopenharmony_ci BINPRM_BUF_SIZE, &pos); 27962306a36Sopenharmony_ci if (unlikely(retval != BINPRM_BUF_SIZE)) { 28062306a36Sopenharmony_ci if (retval >= 0) 28162306a36Sopenharmony_ci retval = -ENOEXEC; 28262306a36Sopenharmony_ci goto error; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci interp_params.hdr = *((struct elfhdr *) bprm->buf); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci case PT_LOAD: 28962306a36Sopenharmony_ci#ifdef CONFIG_MMU 29062306a36Sopenharmony_ci if (exec_params.load_addr == 0) 29162306a36Sopenharmony_ci exec_params.load_addr = phdr->p_vaddr; 29262306a36Sopenharmony_ci#endif 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (is_constdisp(&exec_params.hdr)) 29962306a36Sopenharmony_ci exec_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* perform insanity checks on the interpreter */ 30262306a36Sopenharmony_ci if (interpreter_name) { 30362306a36Sopenharmony_ci retval = -ELIBBAD; 30462306a36Sopenharmony_ci if (!is_elf(&interp_params.hdr, interpreter)) 30562306a36Sopenharmony_ci goto error; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci interp_params.flags = ELF_FDPIC_FLAG_PRESENT; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* read the interpreter's program header table */ 31062306a36Sopenharmony_ci retval = elf_fdpic_fetch_phdrs(&interp_params, interpreter); 31162306a36Sopenharmony_ci if (retval < 0) 31262306a36Sopenharmony_ci goto error; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci stack_size = exec_params.stack_size; 31662306a36Sopenharmony_ci if (exec_params.flags & ELF_FDPIC_FLAG_EXEC_STACK) 31762306a36Sopenharmony_ci executable_stack = EXSTACK_ENABLE_X; 31862306a36Sopenharmony_ci else if (exec_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK) 31962306a36Sopenharmony_ci executable_stack = EXSTACK_DISABLE_X; 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci executable_stack = EXSTACK_DEFAULT; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (stack_size == 0) { 32462306a36Sopenharmony_ci stack_size = interp_params.stack_size; 32562306a36Sopenharmony_ci if (interp_params.flags & ELF_FDPIC_FLAG_EXEC_STACK) 32662306a36Sopenharmony_ci executable_stack = EXSTACK_ENABLE_X; 32762306a36Sopenharmony_ci else if (interp_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK) 32862306a36Sopenharmony_ci executable_stack = EXSTACK_DISABLE_X; 32962306a36Sopenharmony_ci else 33062306a36Sopenharmony_ci executable_stack = EXSTACK_DEFAULT; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci retval = -ENOEXEC; 33462306a36Sopenharmony_ci if (stack_size == 0) 33562306a36Sopenharmony_ci stack_size = 131072UL; /* same as exec.c's default commit */ 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (is_constdisp(&interp_params.hdr)) 33862306a36Sopenharmony_ci interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* flush all traces of the currently running executable */ 34162306a36Sopenharmony_ci retval = begin_new_exec(bprm); 34262306a36Sopenharmony_ci if (retval) 34362306a36Sopenharmony_ci goto error; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* there's now no turning back... the old userspace image is dead, 34662306a36Sopenharmony_ci * defunct, deceased, etc. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci SET_PERSONALITY(exec_params.hdr); 34962306a36Sopenharmony_ci if (elf_check_fdpic(&exec_params.hdr)) 35062306a36Sopenharmony_ci current->personality |= PER_LINUX_FDPIC; 35162306a36Sopenharmony_ci if (elf_read_implies_exec(&exec_params.hdr, executable_stack)) 35262306a36Sopenharmony_ci current->personality |= READ_IMPLIES_EXEC; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci setup_new_exec(bprm); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci set_binfmt(&elf_fdpic_format); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci current->mm->start_code = 0; 35962306a36Sopenharmony_ci current->mm->end_code = 0; 36062306a36Sopenharmony_ci current->mm->start_stack = 0; 36162306a36Sopenharmony_ci current->mm->start_data = 0; 36262306a36Sopenharmony_ci current->mm->end_data = 0; 36362306a36Sopenharmony_ci current->mm->context.exec_fdpic_loadmap = 0; 36462306a36Sopenharmony_ci current->mm->context.interp_fdpic_loadmap = 0; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci#ifdef CONFIG_MMU 36762306a36Sopenharmony_ci elf_fdpic_arch_lay_out_mm(&exec_params, 36862306a36Sopenharmony_ci &interp_params, 36962306a36Sopenharmony_ci ¤t->mm->start_stack, 37062306a36Sopenharmony_ci ¤t->mm->start_brk); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci retval = setup_arg_pages(bprm, current->mm->start_stack, 37362306a36Sopenharmony_ci executable_stack); 37462306a36Sopenharmony_ci if (retval < 0) 37562306a36Sopenharmony_ci goto error; 37662306a36Sopenharmony_ci#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES 37762306a36Sopenharmony_ci retval = arch_setup_additional_pages(bprm, !!interpreter_name); 37862306a36Sopenharmony_ci if (retval < 0) 37962306a36Sopenharmony_ci goto error; 38062306a36Sopenharmony_ci#endif 38162306a36Sopenharmony_ci#endif 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* load the executable and interpreter into memory */ 38462306a36Sopenharmony_ci retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm, 38562306a36Sopenharmony_ci "executable"); 38662306a36Sopenharmony_ci if (retval < 0) 38762306a36Sopenharmony_ci goto error; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (interpreter_name) { 39062306a36Sopenharmony_ci retval = elf_fdpic_map_file(&interp_params, interpreter, 39162306a36Sopenharmony_ci current->mm, "interpreter"); 39262306a36Sopenharmony_ci if (retval < 0) { 39362306a36Sopenharmony_ci printk(KERN_ERR "Unable to load interpreter\n"); 39462306a36Sopenharmony_ci goto error; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci allow_write_access(interpreter); 39862306a36Sopenharmony_ci fput(interpreter); 39962306a36Sopenharmony_ci interpreter = NULL; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci#ifdef CONFIG_MMU 40362306a36Sopenharmony_ci if (!current->mm->start_brk) 40462306a36Sopenharmony_ci current->mm->start_brk = current->mm->end_data; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci current->mm->brk = current->mm->start_brk = 40762306a36Sopenharmony_ci PAGE_ALIGN(current->mm->start_brk); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci#else 41062306a36Sopenharmony_ci /* create a stack area and zero-size brk area */ 41162306a36Sopenharmony_ci stack_size = (stack_size + PAGE_SIZE - 1) & PAGE_MASK; 41262306a36Sopenharmony_ci if (stack_size < PAGE_SIZE * 2) 41362306a36Sopenharmony_ci stack_size = PAGE_SIZE * 2; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci stack_prot = PROT_READ | PROT_WRITE; 41662306a36Sopenharmony_ci if (executable_stack == EXSTACK_ENABLE_X || 41762306a36Sopenharmony_ci (executable_stack == EXSTACK_DEFAULT && VM_STACK_FLAGS & VM_EXEC)) 41862306a36Sopenharmony_ci stack_prot |= PROT_EXEC; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci current->mm->start_brk = vm_mmap(NULL, 0, stack_size, stack_prot, 42162306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS | 42262306a36Sopenharmony_ci MAP_UNINITIALIZED | MAP_GROWSDOWN, 42362306a36Sopenharmony_ci 0); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (IS_ERR_VALUE(current->mm->start_brk)) { 42662306a36Sopenharmony_ci retval = current->mm->start_brk; 42762306a36Sopenharmony_ci current->mm->start_brk = 0; 42862306a36Sopenharmony_ci goto error; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci current->mm->brk = current->mm->start_brk; 43262306a36Sopenharmony_ci current->mm->context.end_brk = current->mm->start_brk; 43362306a36Sopenharmony_ci current->mm->start_stack = current->mm->start_brk + stack_size; 43462306a36Sopenharmony_ci#endif 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci retval = create_elf_fdpic_tables(bprm, current->mm, &exec_params, 43762306a36Sopenharmony_ci &interp_params); 43862306a36Sopenharmony_ci if (retval < 0) 43962306a36Sopenharmony_ci goto error; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci kdebug("- start_code %lx", current->mm->start_code); 44262306a36Sopenharmony_ci kdebug("- end_code %lx", current->mm->end_code); 44362306a36Sopenharmony_ci kdebug("- start_data %lx", current->mm->start_data); 44462306a36Sopenharmony_ci kdebug("- end_data %lx", current->mm->end_data); 44562306a36Sopenharmony_ci kdebug("- start_brk %lx", current->mm->start_brk); 44662306a36Sopenharmony_ci kdebug("- brk %lx", current->mm->brk); 44762306a36Sopenharmony_ci kdebug("- start_stack %lx", current->mm->start_stack); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci#ifdef ELF_FDPIC_PLAT_INIT 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * The ABI may specify that certain registers be set up in special 45262306a36Sopenharmony_ci * ways (on i386 %edx is the address of a DT_FINI function, for 45362306a36Sopenharmony_ci * example. This macro performs whatever initialization to 45462306a36Sopenharmony_ci * the regs structure is required. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci dynaddr = interp_params.dynamic_addr ?: exec_params.dynamic_addr; 45762306a36Sopenharmony_ci ELF_FDPIC_PLAT_INIT(regs, exec_params.map_addr, interp_params.map_addr, 45862306a36Sopenharmony_ci dynaddr); 45962306a36Sopenharmony_ci#endif 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci finalize_exec(bprm); 46262306a36Sopenharmony_ci /* everything is now ready... get the userspace context ready to roll */ 46362306a36Sopenharmony_ci entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; 46462306a36Sopenharmony_ci start_thread(regs, entryaddr, current->mm->start_stack); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci retval = 0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cierror: 46962306a36Sopenharmony_ci if (interpreter) { 47062306a36Sopenharmony_ci allow_write_access(interpreter); 47162306a36Sopenharmony_ci fput(interpreter); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci kfree(interpreter_name); 47462306a36Sopenharmony_ci kfree(exec_params.phdrs); 47562306a36Sopenharmony_ci kfree(exec_params.loadmap); 47662306a36Sopenharmony_ci kfree(interp_params.phdrs); 47762306a36Sopenharmony_ci kfree(interp_params.loadmap); 47862306a36Sopenharmony_ci return retval; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/*****************************************************************************/ 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci#ifndef ELF_BASE_PLATFORM 48462306a36Sopenharmony_ci/* 48562306a36Sopenharmony_ci * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture. 48662306a36Sopenharmony_ci * If the arch defines ELF_BASE_PLATFORM (in asm/elf.h), the value 48762306a36Sopenharmony_ci * will be copied to the user stack in the same manner as AT_PLATFORM. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci#define ELF_BASE_PLATFORM NULL 49062306a36Sopenharmony_ci#endif 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/* 49362306a36Sopenharmony_ci * present useful information to the program by shovelling it onto the new 49462306a36Sopenharmony_ci * process's stack 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_cistatic int create_elf_fdpic_tables(struct linux_binprm *bprm, 49762306a36Sopenharmony_ci struct mm_struct *mm, 49862306a36Sopenharmony_ci struct elf_fdpic_params *exec_params, 49962306a36Sopenharmony_ci struct elf_fdpic_params *interp_params) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci const struct cred *cred = current_cred(); 50262306a36Sopenharmony_ci unsigned long sp, csp, nitems; 50362306a36Sopenharmony_ci elf_caddr_t __user *argv, *envp; 50462306a36Sopenharmony_ci size_t platform_len = 0, len; 50562306a36Sopenharmony_ci char *k_platform, *k_base_platform; 50662306a36Sopenharmony_ci char __user *u_platform, *u_base_platform, *p; 50762306a36Sopenharmony_ci int loop; 50862306a36Sopenharmony_ci int nr; /* reset for each csp adjustment */ 50962306a36Sopenharmony_ci unsigned long flags = 0; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci#ifdef CONFIG_MMU 51262306a36Sopenharmony_ci /* In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions 51362306a36Sopenharmony_ci * by the processes running on the same package. One thing we can do is 51462306a36Sopenharmony_ci * to shuffle the initial stack for them, so we give the architecture 51562306a36Sopenharmony_ci * an opportunity to do so here. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci sp = arch_align_stack(bprm->p); 51862306a36Sopenharmony_ci#else 51962306a36Sopenharmony_ci sp = mm->start_stack; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* stack the program arguments and environment */ 52262306a36Sopenharmony_ci if (transfer_args_to_stack(bprm, &sp) < 0) 52362306a36Sopenharmony_ci return -EFAULT; 52462306a36Sopenharmony_ci sp &= ~15; 52562306a36Sopenharmony_ci#endif 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * If this architecture has a platform capability string, copy it 52962306a36Sopenharmony_ci * to userspace. In some cases (Sparc), this info is impossible 53062306a36Sopenharmony_ci * for userspace to get any other way, in others (i386) it is 53162306a36Sopenharmony_ci * merely difficult. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci k_platform = ELF_PLATFORM; 53462306a36Sopenharmony_ci u_platform = NULL; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (k_platform) { 53762306a36Sopenharmony_ci platform_len = strlen(k_platform) + 1; 53862306a36Sopenharmony_ci sp -= platform_len; 53962306a36Sopenharmony_ci u_platform = (char __user *) sp; 54062306a36Sopenharmony_ci if (copy_to_user(u_platform, k_platform, platform_len) != 0) 54162306a36Sopenharmony_ci return -EFAULT; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * If this architecture has a "base" platform capability 54662306a36Sopenharmony_ci * string, copy it to userspace. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci k_base_platform = ELF_BASE_PLATFORM; 54962306a36Sopenharmony_ci u_base_platform = NULL; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (k_base_platform) { 55262306a36Sopenharmony_ci platform_len = strlen(k_base_platform) + 1; 55362306a36Sopenharmony_ci sp -= platform_len; 55462306a36Sopenharmony_ci u_base_platform = (char __user *) sp; 55562306a36Sopenharmony_ci if (copy_to_user(u_base_platform, k_base_platform, platform_len) != 0) 55662306a36Sopenharmony_ci return -EFAULT; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci sp &= ~7UL; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* stack the load map(s) */ 56262306a36Sopenharmony_ci len = sizeof(struct elf_fdpic_loadmap); 56362306a36Sopenharmony_ci len += sizeof(struct elf_fdpic_loadseg) * exec_params->loadmap->nsegs; 56462306a36Sopenharmony_ci sp = (sp - len) & ~7UL; 56562306a36Sopenharmony_ci exec_params->map_addr = sp; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (copy_to_user((void __user *) sp, exec_params->loadmap, len) != 0) 56862306a36Sopenharmony_ci return -EFAULT; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci current->mm->context.exec_fdpic_loadmap = (unsigned long) sp; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (interp_params->loadmap) { 57362306a36Sopenharmony_ci len = sizeof(struct elf_fdpic_loadmap); 57462306a36Sopenharmony_ci len += sizeof(struct elf_fdpic_loadseg) * 57562306a36Sopenharmony_ci interp_params->loadmap->nsegs; 57662306a36Sopenharmony_ci sp = (sp - len) & ~7UL; 57762306a36Sopenharmony_ci interp_params->map_addr = sp; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (copy_to_user((void __user *) sp, interp_params->loadmap, 58062306a36Sopenharmony_ci len) != 0) 58162306a36Sopenharmony_ci return -EFAULT; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci current->mm->context.interp_fdpic_loadmap = (unsigned long) sp; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* force 16 byte _final_ alignment here for generality */ 58762306a36Sopenharmony_ci#define DLINFO_ITEMS 15 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0) + 59062306a36Sopenharmony_ci (k_base_platform ? 1 : 0) + AT_VECTOR_SIZE_ARCH; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (bprm->have_execfd) 59362306a36Sopenharmony_ci nitems++; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci csp = sp; 59662306a36Sopenharmony_ci sp -= nitems * 2 * sizeof(unsigned long); 59762306a36Sopenharmony_ci sp -= (bprm->envc + 1) * sizeof(char *); /* envv[] */ 59862306a36Sopenharmony_ci sp -= (bprm->argc + 1) * sizeof(char *); /* argv[] */ 59962306a36Sopenharmony_ci sp -= 1 * sizeof(unsigned long); /* argc */ 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci csp -= sp & 15UL; 60262306a36Sopenharmony_ci sp -= sp & 15UL; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* put the ELF interpreter info on the stack */ 60562306a36Sopenharmony_ci#define NEW_AUX_ENT(id, val) \ 60662306a36Sopenharmony_ci do { \ 60762306a36Sopenharmony_ci struct { unsigned long _id, _val; } __user *ent, v; \ 60862306a36Sopenharmony_ci \ 60962306a36Sopenharmony_ci ent = (void __user *) csp; \ 61062306a36Sopenharmony_ci v._id = (id); \ 61162306a36Sopenharmony_ci v._val = (val); \ 61262306a36Sopenharmony_ci if (copy_to_user(ent + nr, &v, sizeof(v))) \ 61362306a36Sopenharmony_ci return -EFAULT; \ 61462306a36Sopenharmony_ci nr++; \ 61562306a36Sopenharmony_ci } while (0) 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci nr = 0; 61862306a36Sopenharmony_ci csp -= 2 * sizeof(unsigned long); 61962306a36Sopenharmony_ci NEW_AUX_ENT(AT_NULL, 0); 62062306a36Sopenharmony_ci if (k_platform) { 62162306a36Sopenharmony_ci nr = 0; 62262306a36Sopenharmony_ci csp -= 2 * sizeof(unsigned long); 62362306a36Sopenharmony_ci NEW_AUX_ENT(AT_PLATFORM, 62462306a36Sopenharmony_ci (elf_addr_t) (unsigned long) u_platform); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (k_base_platform) { 62862306a36Sopenharmony_ci nr = 0; 62962306a36Sopenharmony_ci csp -= 2 * sizeof(unsigned long); 63062306a36Sopenharmony_ci NEW_AUX_ENT(AT_BASE_PLATFORM, 63162306a36Sopenharmony_ci (elf_addr_t) (unsigned long) u_base_platform); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (bprm->have_execfd) { 63562306a36Sopenharmony_ci nr = 0; 63662306a36Sopenharmony_ci csp -= 2 * sizeof(unsigned long); 63762306a36Sopenharmony_ci NEW_AUX_ENT(AT_EXECFD, bprm->execfd); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci nr = 0; 64162306a36Sopenharmony_ci csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long); 64262306a36Sopenharmony_ci NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP); 64362306a36Sopenharmony_ci#ifdef ELF_HWCAP2 64462306a36Sopenharmony_ci NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2); 64562306a36Sopenharmony_ci#endif 64662306a36Sopenharmony_ci NEW_AUX_ENT(AT_PAGESZ, PAGE_SIZE); 64762306a36Sopenharmony_ci NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC); 64862306a36Sopenharmony_ci NEW_AUX_ENT(AT_PHDR, exec_params->ph_addr); 64962306a36Sopenharmony_ci NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr)); 65062306a36Sopenharmony_ci NEW_AUX_ENT(AT_PHNUM, exec_params->hdr.e_phnum); 65162306a36Sopenharmony_ci NEW_AUX_ENT(AT_BASE, interp_params->elfhdr_addr); 65262306a36Sopenharmony_ci if (bprm->interp_flags & BINPRM_FLAGS_PRESERVE_ARGV0) 65362306a36Sopenharmony_ci flags |= AT_FLAGS_PRESERVE_ARGV0; 65462306a36Sopenharmony_ci NEW_AUX_ENT(AT_FLAGS, flags); 65562306a36Sopenharmony_ci NEW_AUX_ENT(AT_ENTRY, exec_params->entry_addr); 65662306a36Sopenharmony_ci NEW_AUX_ENT(AT_UID, (elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid)); 65762306a36Sopenharmony_ci NEW_AUX_ENT(AT_EUID, (elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid)); 65862306a36Sopenharmony_ci NEW_AUX_ENT(AT_GID, (elf_addr_t) from_kgid_munged(cred->user_ns, cred->gid)); 65962306a36Sopenharmony_ci NEW_AUX_ENT(AT_EGID, (elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid)); 66062306a36Sopenharmony_ci NEW_AUX_ENT(AT_SECURE, bprm->secureexec); 66162306a36Sopenharmony_ci NEW_AUX_ENT(AT_EXECFN, bprm->exec); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci#ifdef ARCH_DLINFO 66462306a36Sopenharmony_ci nr = 0; 66562306a36Sopenharmony_ci csp -= AT_VECTOR_SIZE_ARCH * 2 * sizeof(unsigned long); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* ARCH_DLINFO must come last so platform specific code can enforce 66862306a36Sopenharmony_ci * special alignment requirements on the AUXV if necessary (eg. PPC). 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci ARCH_DLINFO; 67162306a36Sopenharmony_ci#endif 67262306a36Sopenharmony_ci#undef NEW_AUX_ENT 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* allocate room for argv[] and envv[] */ 67562306a36Sopenharmony_ci csp -= (bprm->envc + 1) * sizeof(elf_caddr_t); 67662306a36Sopenharmony_ci envp = (elf_caddr_t __user *) csp; 67762306a36Sopenharmony_ci csp -= (bprm->argc + 1) * sizeof(elf_caddr_t); 67862306a36Sopenharmony_ci argv = (elf_caddr_t __user *) csp; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* stack argc */ 68162306a36Sopenharmony_ci csp -= sizeof(unsigned long); 68262306a36Sopenharmony_ci if (put_user(bprm->argc, (unsigned long __user *) csp)) 68362306a36Sopenharmony_ci return -EFAULT; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci BUG_ON(csp != sp); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* fill in the argv[] array */ 68862306a36Sopenharmony_ci#ifdef CONFIG_MMU 68962306a36Sopenharmony_ci current->mm->arg_start = bprm->p; 69062306a36Sopenharmony_ci#else 69162306a36Sopenharmony_ci current->mm->arg_start = current->mm->start_stack - 69262306a36Sopenharmony_ci (MAX_ARG_PAGES * PAGE_SIZE - bprm->p); 69362306a36Sopenharmony_ci#endif 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci p = (char __user *) current->mm->arg_start; 69662306a36Sopenharmony_ci for (loop = bprm->argc; loop > 0; loop--) { 69762306a36Sopenharmony_ci if (put_user((elf_caddr_t) p, argv++)) 69862306a36Sopenharmony_ci return -EFAULT; 69962306a36Sopenharmony_ci len = strnlen_user(p, MAX_ARG_STRLEN); 70062306a36Sopenharmony_ci if (!len || len > MAX_ARG_STRLEN) 70162306a36Sopenharmony_ci return -EINVAL; 70262306a36Sopenharmony_ci p += len; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci if (put_user(NULL, argv)) 70562306a36Sopenharmony_ci return -EFAULT; 70662306a36Sopenharmony_ci current->mm->arg_end = (unsigned long) p; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* fill in the envv[] array */ 70962306a36Sopenharmony_ci current->mm->env_start = (unsigned long) p; 71062306a36Sopenharmony_ci for (loop = bprm->envc; loop > 0; loop--) { 71162306a36Sopenharmony_ci if (put_user((elf_caddr_t)(unsigned long) p, envp++)) 71262306a36Sopenharmony_ci return -EFAULT; 71362306a36Sopenharmony_ci len = strnlen_user(p, MAX_ARG_STRLEN); 71462306a36Sopenharmony_ci if (!len || len > MAX_ARG_STRLEN) 71562306a36Sopenharmony_ci return -EINVAL; 71662306a36Sopenharmony_ci p += len; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci if (put_user(NULL, envp)) 71962306a36Sopenharmony_ci return -EFAULT; 72062306a36Sopenharmony_ci current->mm->env_end = (unsigned long) p; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci mm->start_stack = (unsigned long) sp; 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci/*****************************************************************************/ 72762306a36Sopenharmony_ci/* 72862306a36Sopenharmony_ci * load the appropriate binary image (executable or interpreter) into memory 72962306a36Sopenharmony_ci * - we assume no MMU is available 73062306a36Sopenharmony_ci * - if no other PIC bits are set in params->hdr->e_flags 73162306a36Sopenharmony_ci * - we assume that the LOADable segments in the binary are independently relocatable 73262306a36Sopenharmony_ci * - we assume R/O executable segments are shareable 73362306a36Sopenharmony_ci * - else 73462306a36Sopenharmony_ci * - we assume the loadable parts of the image to require fixed displacement 73562306a36Sopenharmony_ci * - the image is not shareable 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_cistatic int elf_fdpic_map_file(struct elf_fdpic_params *params, 73862306a36Sopenharmony_ci struct file *file, 73962306a36Sopenharmony_ci struct mm_struct *mm, 74062306a36Sopenharmony_ci const char *what) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct elf_fdpic_loadmap *loadmap; 74362306a36Sopenharmony_ci#ifdef CONFIG_MMU 74462306a36Sopenharmony_ci struct elf_fdpic_loadseg *mseg; 74562306a36Sopenharmony_ci unsigned long load_addr; 74662306a36Sopenharmony_ci#endif 74762306a36Sopenharmony_ci struct elf_fdpic_loadseg *seg; 74862306a36Sopenharmony_ci struct elf_phdr *phdr; 74962306a36Sopenharmony_ci unsigned nloads, tmp; 75062306a36Sopenharmony_ci unsigned long stop; 75162306a36Sopenharmony_ci int loop, ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* allocate a load map table */ 75462306a36Sopenharmony_ci nloads = 0; 75562306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++) 75662306a36Sopenharmony_ci if (params->phdrs[loop].p_type == PT_LOAD) 75762306a36Sopenharmony_ci nloads++; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (nloads == 0) 76062306a36Sopenharmony_ci return -ELIBBAD; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci loadmap = kzalloc(struct_size(loadmap, segs, nloads), GFP_KERNEL); 76362306a36Sopenharmony_ci if (!loadmap) 76462306a36Sopenharmony_ci return -ENOMEM; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci params->loadmap = loadmap; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci loadmap->version = ELF_FDPIC_LOADMAP_VERSION; 76962306a36Sopenharmony_ci loadmap->nsegs = nloads; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* map the requested LOADs into the memory space */ 77262306a36Sopenharmony_ci switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) { 77362306a36Sopenharmony_ci case ELF_FDPIC_FLAG_CONSTDISP: 77462306a36Sopenharmony_ci case ELF_FDPIC_FLAG_CONTIGUOUS: 77562306a36Sopenharmony_ci#ifndef CONFIG_MMU 77662306a36Sopenharmony_ci ret = elf_fdpic_map_file_constdisp_on_uclinux(params, file, mm); 77762306a36Sopenharmony_ci if (ret < 0) 77862306a36Sopenharmony_ci return ret; 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci#endif 78162306a36Sopenharmony_ci default: 78262306a36Sopenharmony_ci ret = elf_fdpic_map_file_by_direct_mmap(params, file, mm); 78362306a36Sopenharmony_ci if (ret < 0) 78462306a36Sopenharmony_ci return ret; 78562306a36Sopenharmony_ci break; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* map the entry point */ 78962306a36Sopenharmony_ci if (params->hdr.e_entry) { 79062306a36Sopenharmony_ci seg = loadmap->segs; 79162306a36Sopenharmony_ci for (loop = loadmap->nsegs; loop > 0; loop--, seg++) { 79262306a36Sopenharmony_ci if (params->hdr.e_entry >= seg->p_vaddr && 79362306a36Sopenharmony_ci params->hdr.e_entry < seg->p_vaddr + seg->p_memsz) { 79462306a36Sopenharmony_ci params->entry_addr = 79562306a36Sopenharmony_ci (params->hdr.e_entry - seg->p_vaddr) + 79662306a36Sopenharmony_ci seg->addr; 79762306a36Sopenharmony_ci break; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* determine where the program header table has wound up if mapped */ 80362306a36Sopenharmony_ci stop = params->hdr.e_phoff; 80462306a36Sopenharmony_ci stop += params->hdr.e_phnum * sizeof (struct elf_phdr); 80562306a36Sopenharmony_ci phdr = params->phdrs; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { 80862306a36Sopenharmony_ci if (phdr->p_type != PT_LOAD) 80962306a36Sopenharmony_ci continue; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (phdr->p_offset > params->hdr.e_phoff || 81262306a36Sopenharmony_ci phdr->p_offset + phdr->p_filesz < stop) 81362306a36Sopenharmony_ci continue; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci seg = loadmap->segs; 81662306a36Sopenharmony_ci for (loop = loadmap->nsegs; loop > 0; loop--, seg++) { 81762306a36Sopenharmony_ci if (phdr->p_vaddr >= seg->p_vaddr && 81862306a36Sopenharmony_ci phdr->p_vaddr + phdr->p_filesz <= 81962306a36Sopenharmony_ci seg->p_vaddr + seg->p_memsz) { 82062306a36Sopenharmony_ci params->ph_addr = 82162306a36Sopenharmony_ci (phdr->p_vaddr - seg->p_vaddr) + 82262306a36Sopenharmony_ci seg->addr + 82362306a36Sopenharmony_ci params->hdr.e_phoff - phdr->p_offset; 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci break; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* determine where the dynamic section has wound up if there is one */ 83162306a36Sopenharmony_ci phdr = params->phdrs; 83262306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { 83362306a36Sopenharmony_ci if (phdr->p_type != PT_DYNAMIC) 83462306a36Sopenharmony_ci continue; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci seg = loadmap->segs; 83762306a36Sopenharmony_ci for (loop = loadmap->nsegs; loop > 0; loop--, seg++) { 83862306a36Sopenharmony_ci if (phdr->p_vaddr >= seg->p_vaddr && 83962306a36Sopenharmony_ci phdr->p_vaddr + phdr->p_memsz <= 84062306a36Sopenharmony_ci seg->p_vaddr + seg->p_memsz) { 84162306a36Sopenharmony_ci Elf_Dyn __user *dyn; 84262306a36Sopenharmony_ci Elf_Sword d_tag; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci params->dynamic_addr = 84562306a36Sopenharmony_ci (phdr->p_vaddr - seg->p_vaddr) + 84662306a36Sopenharmony_ci seg->addr; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* check the dynamic section contains at least 84962306a36Sopenharmony_ci * one item, and that the last item is a NULL 85062306a36Sopenharmony_ci * entry */ 85162306a36Sopenharmony_ci if (phdr->p_memsz == 0 || 85262306a36Sopenharmony_ci phdr->p_memsz % sizeof(Elf_Dyn) != 0) 85362306a36Sopenharmony_ci goto dynamic_error; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci tmp = phdr->p_memsz / sizeof(Elf_Dyn); 85662306a36Sopenharmony_ci dyn = (Elf_Dyn __user *)params->dynamic_addr; 85762306a36Sopenharmony_ci if (get_user(d_tag, &dyn[tmp - 1].d_tag) || 85862306a36Sopenharmony_ci d_tag != 0) 85962306a36Sopenharmony_ci goto dynamic_error; 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* now elide adjacent segments in the load map on MMU linux 86762306a36Sopenharmony_ci * - on uClinux the holes between may actually be filled with system 86862306a36Sopenharmony_ci * stuff or stuff from other processes 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci#ifdef CONFIG_MMU 87162306a36Sopenharmony_ci nloads = loadmap->nsegs; 87262306a36Sopenharmony_ci mseg = loadmap->segs; 87362306a36Sopenharmony_ci seg = mseg + 1; 87462306a36Sopenharmony_ci for (loop = 1; loop < nloads; loop++) { 87562306a36Sopenharmony_ci /* see if we have a candidate for merging */ 87662306a36Sopenharmony_ci if (seg->p_vaddr - mseg->p_vaddr == seg->addr - mseg->addr) { 87762306a36Sopenharmony_ci load_addr = PAGE_ALIGN(mseg->addr + mseg->p_memsz); 87862306a36Sopenharmony_ci if (load_addr == (seg->addr & PAGE_MASK)) { 87962306a36Sopenharmony_ci mseg->p_memsz += 88062306a36Sopenharmony_ci load_addr - 88162306a36Sopenharmony_ci (mseg->addr + mseg->p_memsz); 88262306a36Sopenharmony_ci mseg->p_memsz += seg->addr & ~PAGE_MASK; 88362306a36Sopenharmony_ci mseg->p_memsz += seg->p_memsz; 88462306a36Sopenharmony_ci loadmap->nsegs--; 88562306a36Sopenharmony_ci continue; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci mseg++; 89062306a36Sopenharmony_ci if (mseg != seg) 89162306a36Sopenharmony_ci *mseg = *seg; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci#endif 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci kdebug("Mapped Object [%s]:", what); 89662306a36Sopenharmony_ci kdebug("- elfhdr : %lx", params->elfhdr_addr); 89762306a36Sopenharmony_ci kdebug("- entry : %lx", params->entry_addr); 89862306a36Sopenharmony_ci kdebug("- PHDR[] : %lx", params->ph_addr); 89962306a36Sopenharmony_ci kdebug("- DYNAMIC[]: %lx", params->dynamic_addr); 90062306a36Sopenharmony_ci seg = loadmap->segs; 90162306a36Sopenharmony_ci for (loop = 0; loop < loadmap->nsegs; loop++, seg++) 90262306a36Sopenharmony_ci kdebug("- LOAD[%d] : %08x-%08x [va=%x ms=%x]", 90362306a36Sopenharmony_ci loop, 90462306a36Sopenharmony_ci seg->addr, seg->addr + seg->p_memsz - 1, 90562306a36Sopenharmony_ci seg->p_vaddr, seg->p_memsz); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci return 0; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cidynamic_error: 91062306a36Sopenharmony_ci printk("ELF FDPIC %s with invalid DYNAMIC section (inode=%lu)\n", 91162306a36Sopenharmony_ci what, file_inode(file)->i_ino); 91262306a36Sopenharmony_ci return -ELIBBAD; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/*****************************************************************************/ 91662306a36Sopenharmony_ci/* 91762306a36Sopenharmony_ci * map a file with constant displacement under uClinux 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci#ifndef CONFIG_MMU 92062306a36Sopenharmony_cistatic int elf_fdpic_map_file_constdisp_on_uclinux( 92162306a36Sopenharmony_ci struct elf_fdpic_params *params, 92262306a36Sopenharmony_ci struct file *file, 92362306a36Sopenharmony_ci struct mm_struct *mm) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct elf_fdpic_loadseg *seg; 92662306a36Sopenharmony_ci struct elf_phdr *phdr; 92762306a36Sopenharmony_ci unsigned long load_addr, base = ULONG_MAX, top = 0, maddr = 0; 92862306a36Sopenharmony_ci int loop, ret; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci load_addr = params->load_addr; 93162306a36Sopenharmony_ci seg = params->loadmap->segs; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* determine the bounds of the contiguous overall allocation we must 93462306a36Sopenharmony_ci * make */ 93562306a36Sopenharmony_ci phdr = params->phdrs; 93662306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { 93762306a36Sopenharmony_ci if (params->phdrs[loop].p_type != PT_LOAD) 93862306a36Sopenharmony_ci continue; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (base > phdr->p_vaddr) 94162306a36Sopenharmony_ci base = phdr->p_vaddr; 94262306a36Sopenharmony_ci if (top < phdr->p_vaddr + phdr->p_memsz) 94362306a36Sopenharmony_ci top = phdr->p_vaddr + phdr->p_memsz; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* allocate one big anon block for everything */ 94762306a36Sopenharmony_ci maddr = vm_mmap(NULL, load_addr, top - base, 94862306a36Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, 0); 94962306a36Sopenharmony_ci if (IS_ERR_VALUE(maddr)) 95062306a36Sopenharmony_ci return (int) maddr; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (load_addr != 0) 95362306a36Sopenharmony_ci load_addr += PAGE_ALIGN(top - base); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* and then load the file segments into it */ 95662306a36Sopenharmony_ci phdr = params->phdrs; 95762306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { 95862306a36Sopenharmony_ci if (params->phdrs[loop].p_type != PT_LOAD) 95962306a36Sopenharmony_ci continue; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci seg->addr = maddr + (phdr->p_vaddr - base); 96262306a36Sopenharmony_ci seg->p_vaddr = phdr->p_vaddr; 96362306a36Sopenharmony_ci seg->p_memsz = phdr->p_memsz; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ret = read_code(file, seg->addr, phdr->p_offset, 96662306a36Sopenharmony_ci phdr->p_filesz); 96762306a36Sopenharmony_ci if (ret < 0) 96862306a36Sopenharmony_ci return ret; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* map the ELF header address if in this segment */ 97162306a36Sopenharmony_ci if (phdr->p_offset == 0) 97262306a36Sopenharmony_ci params->elfhdr_addr = seg->addr; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* clear any space allocated but not loaded */ 97562306a36Sopenharmony_ci if (phdr->p_filesz < phdr->p_memsz) { 97662306a36Sopenharmony_ci if (clear_user((void *) (seg->addr + phdr->p_filesz), 97762306a36Sopenharmony_ci phdr->p_memsz - phdr->p_filesz)) 97862306a36Sopenharmony_ci return -EFAULT; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (mm) { 98262306a36Sopenharmony_ci if (phdr->p_flags & PF_X) { 98362306a36Sopenharmony_ci if (!mm->start_code) { 98462306a36Sopenharmony_ci mm->start_code = seg->addr; 98562306a36Sopenharmony_ci mm->end_code = seg->addr + 98662306a36Sopenharmony_ci phdr->p_memsz; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci } else if (!mm->start_data) { 98962306a36Sopenharmony_ci mm->start_data = seg->addr; 99062306a36Sopenharmony_ci mm->end_data = seg->addr + phdr->p_memsz; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci seg++; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return 0; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci#endif 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/*****************************************************************************/ 100262306a36Sopenharmony_ci/* 100362306a36Sopenharmony_ci * map a binary by direct mmap() of the individual PT_LOAD segments 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_cistatic int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, 100662306a36Sopenharmony_ci struct file *file, 100762306a36Sopenharmony_ci struct mm_struct *mm) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct elf_fdpic_loadseg *seg; 101062306a36Sopenharmony_ci struct elf_phdr *phdr; 101162306a36Sopenharmony_ci unsigned long load_addr, delta_vaddr; 101262306a36Sopenharmony_ci int loop, dvset; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci load_addr = params->load_addr; 101562306a36Sopenharmony_ci delta_vaddr = 0; 101662306a36Sopenharmony_ci dvset = 0; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci seg = params->loadmap->segs; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* deal with each load segment separately */ 102162306a36Sopenharmony_ci phdr = params->phdrs; 102262306a36Sopenharmony_ci for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { 102362306a36Sopenharmony_ci unsigned long maddr, disp, excess, excess1; 102462306a36Sopenharmony_ci int prot = 0, flags; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (phdr->p_type != PT_LOAD) 102762306a36Sopenharmony_ci continue; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci kdebug("[LOAD] va=%lx of=%lx fs=%lx ms=%lx", 103062306a36Sopenharmony_ci (unsigned long) phdr->p_vaddr, 103162306a36Sopenharmony_ci (unsigned long) phdr->p_offset, 103262306a36Sopenharmony_ci (unsigned long) phdr->p_filesz, 103362306a36Sopenharmony_ci (unsigned long) phdr->p_memsz); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* determine the mapping parameters */ 103662306a36Sopenharmony_ci if (phdr->p_flags & PF_R) prot |= PROT_READ; 103762306a36Sopenharmony_ci if (phdr->p_flags & PF_W) prot |= PROT_WRITE; 103862306a36Sopenharmony_ci if (phdr->p_flags & PF_X) prot |= PROT_EXEC; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci flags = MAP_PRIVATE; 104162306a36Sopenharmony_ci maddr = 0; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) { 104462306a36Sopenharmony_ci case ELF_FDPIC_FLAG_INDEPENDENT: 104562306a36Sopenharmony_ci /* PT_LOADs are independently locatable */ 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci case ELF_FDPIC_FLAG_HONOURVADDR: 104962306a36Sopenharmony_ci /* the specified virtual address must be honoured */ 105062306a36Sopenharmony_ci maddr = phdr->p_vaddr; 105162306a36Sopenharmony_ci flags |= MAP_FIXED; 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci case ELF_FDPIC_FLAG_CONSTDISP: 105562306a36Sopenharmony_ci /* constant displacement 105662306a36Sopenharmony_ci * - can be mapped anywhere, but must be mapped as a 105762306a36Sopenharmony_ci * unit 105862306a36Sopenharmony_ci */ 105962306a36Sopenharmony_ci if (!dvset) { 106062306a36Sopenharmony_ci maddr = load_addr; 106162306a36Sopenharmony_ci delta_vaddr = phdr->p_vaddr; 106262306a36Sopenharmony_ci dvset = 1; 106362306a36Sopenharmony_ci } else { 106462306a36Sopenharmony_ci maddr = load_addr + phdr->p_vaddr - delta_vaddr; 106562306a36Sopenharmony_ci flags |= MAP_FIXED; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci case ELF_FDPIC_FLAG_CONTIGUOUS: 107062306a36Sopenharmony_ci /* contiguity handled later */ 107162306a36Sopenharmony_ci break; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci default: 107462306a36Sopenharmony_ci BUG(); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci maddr &= PAGE_MASK; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* create the mapping */ 108062306a36Sopenharmony_ci disp = phdr->p_vaddr & ~PAGE_MASK; 108162306a36Sopenharmony_ci maddr = vm_mmap(file, maddr, phdr->p_memsz + disp, prot, flags, 108262306a36Sopenharmony_ci phdr->p_offset - disp); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci kdebug("mmap[%d] <file> sz=%lx pr=%x fl=%x of=%lx --> %08lx", 108562306a36Sopenharmony_ci loop, phdr->p_memsz + disp, prot, flags, 108662306a36Sopenharmony_ci phdr->p_offset - disp, maddr); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (IS_ERR_VALUE(maddr)) 108962306a36Sopenharmony_ci return (int) maddr; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == 109262306a36Sopenharmony_ci ELF_FDPIC_FLAG_CONTIGUOUS) 109362306a36Sopenharmony_ci load_addr += PAGE_ALIGN(phdr->p_memsz + disp); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci seg->addr = maddr + disp; 109662306a36Sopenharmony_ci seg->p_vaddr = phdr->p_vaddr; 109762306a36Sopenharmony_ci seg->p_memsz = phdr->p_memsz; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* map the ELF header address if in this segment */ 110062306a36Sopenharmony_ci if (phdr->p_offset == 0) 110162306a36Sopenharmony_ci params->elfhdr_addr = seg->addr; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* clear the bit between beginning of mapping and beginning of 110462306a36Sopenharmony_ci * PT_LOAD */ 110562306a36Sopenharmony_ci if (prot & PROT_WRITE && disp > 0) { 110662306a36Sopenharmony_ci kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp); 110762306a36Sopenharmony_ci if (clear_user((void __user *) maddr, disp)) 110862306a36Sopenharmony_ci return -EFAULT; 110962306a36Sopenharmony_ci maddr += disp; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* clear any space allocated but not loaded 111362306a36Sopenharmony_ci * - on uClinux we can just clear the lot 111462306a36Sopenharmony_ci * - on MMU linux we'll get a SIGBUS beyond the last page 111562306a36Sopenharmony_ci * extant in the file 111662306a36Sopenharmony_ci */ 111762306a36Sopenharmony_ci excess = phdr->p_memsz - phdr->p_filesz; 111862306a36Sopenharmony_ci excess1 = PAGE_SIZE - ((maddr + phdr->p_filesz) & ~PAGE_MASK); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci#ifdef CONFIG_MMU 112162306a36Sopenharmony_ci if (excess > excess1) { 112262306a36Sopenharmony_ci unsigned long xaddr = maddr + phdr->p_filesz + excess1; 112362306a36Sopenharmony_ci unsigned long xmaddr; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci flags |= MAP_FIXED | MAP_ANONYMOUS; 112662306a36Sopenharmony_ci xmaddr = vm_mmap(NULL, xaddr, excess - excess1, 112762306a36Sopenharmony_ci prot, flags, 0); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci kdebug("mmap[%d] <anon>" 113062306a36Sopenharmony_ci " ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx", 113162306a36Sopenharmony_ci loop, xaddr, excess - excess1, prot, flags, 113262306a36Sopenharmony_ci xmaddr); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (xmaddr != xaddr) 113562306a36Sopenharmony_ci return -ENOMEM; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (prot & PROT_WRITE && excess1 > 0) { 113962306a36Sopenharmony_ci kdebug("clear[%d] ad=%lx sz=%lx", 114062306a36Sopenharmony_ci loop, maddr + phdr->p_filesz, excess1); 114162306a36Sopenharmony_ci if (clear_user((void __user *) maddr + phdr->p_filesz, 114262306a36Sopenharmony_ci excess1)) 114362306a36Sopenharmony_ci return -EFAULT; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci#else 114762306a36Sopenharmony_ci if (excess > 0) { 114862306a36Sopenharmony_ci kdebug("clear[%d] ad=%lx sz=%lx", 114962306a36Sopenharmony_ci loop, maddr + phdr->p_filesz, excess); 115062306a36Sopenharmony_ci if (clear_user((void *) maddr + phdr->p_filesz, excess)) 115162306a36Sopenharmony_ci return -EFAULT; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci#endif 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (mm) { 115662306a36Sopenharmony_ci if (phdr->p_flags & PF_X) { 115762306a36Sopenharmony_ci if (!mm->start_code) { 115862306a36Sopenharmony_ci mm->start_code = maddr; 115962306a36Sopenharmony_ci mm->end_code = maddr + phdr->p_memsz; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci } else if (!mm->start_data) { 116262306a36Sopenharmony_ci mm->start_data = maddr; 116362306a36Sopenharmony_ci mm->end_data = maddr + phdr->p_memsz; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci seg++; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci return 0; 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci/*****************************************************************************/ 117462306a36Sopenharmony_ci/* 117562306a36Sopenharmony_ci * ELF-FDPIC core dumper 117662306a36Sopenharmony_ci * 117762306a36Sopenharmony_ci * Modelled on fs/exec.c:aout_core_dump() 117862306a36Sopenharmony_ci * Jeremy Fitzhardinge <jeremy@sw.oz.au> 117962306a36Sopenharmony_ci * 118062306a36Sopenharmony_ci * Modelled on fs/binfmt_elf.c core dumper 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci#ifdef CONFIG_ELF_CORE 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistruct elf_prstatus_fdpic 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct elf_prstatus_common common; 118762306a36Sopenharmony_ci elf_gregset_t pr_reg; /* GP registers */ 118862306a36Sopenharmony_ci /* When using FDPIC, the loadmap addresses need to be communicated 118962306a36Sopenharmony_ci * to GDB in order for GDB to do the necessary relocations. The 119062306a36Sopenharmony_ci * fields (below) used to communicate this information are placed 119162306a36Sopenharmony_ci * immediately after ``pr_reg'', so that the loadmap addresses may 119262306a36Sopenharmony_ci * be viewed as part of the register set if so desired. 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ci unsigned long pr_exec_fdpic_loadmap; 119562306a36Sopenharmony_ci unsigned long pr_interp_fdpic_loadmap; 119662306a36Sopenharmony_ci int pr_fpvalid; /* True if math co-processor being used. */ 119762306a36Sopenharmony_ci}; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci/* An ELF note in memory */ 120062306a36Sopenharmony_cistruct memelfnote 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci const char *name; 120362306a36Sopenharmony_ci int type; 120462306a36Sopenharmony_ci unsigned int datasz; 120562306a36Sopenharmony_ci void *data; 120662306a36Sopenharmony_ci}; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic int notesize(struct memelfnote *en) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci int sz; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci sz = sizeof(struct elf_note); 121362306a36Sopenharmony_ci sz += roundup(strlen(en->name) + 1, 4); 121462306a36Sopenharmony_ci sz += roundup(en->datasz, 4); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return sz; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci/* #define DEBUG */ 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic int writenote(struct memelfnote *men, struct coredump_params *cprm) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci struct elf_note en; 122462306a36Sopenharmony_ci en.n_namesz = strlen(men->name) + 1; 122562306a36Sopenharmony_ci en.n_descsz = men->datasz; 122662306a36Sopenharmony_ci en.n_type = men->type; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci return dump_emit(cprm, &en, sizeof(en)) && 122962306a36Sopenharmony_ci dump_emit(cprm, men->name, en.n_namesz) && dump_align(cprm, 4) && 123062306a36Sopenharmony_ci dump_emit(cprm, men->data, men->datasz) && dump_align(cprm, 4); 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci memcpy(elf->e_ident, ELFMAG, SELFMAG); 123662306a36Sopenharmony_ci elf->e_ident[EI_CLASS] = ELF_CLASS; 123762306a36Sopenharmony_ci elf->e_ident[EI_DATA] = ELF_DATA; 123862306a36Sopenharmony_ci elf->e_ident[EI_VERSION] = EV_CURRENT; 123962306a36Sopenharmony_ci elf->e_ident[EI_OSABI] = ELF_OSABI; 124062306a36Sopenharmony_ci memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci elf->e_type = ET_CORE; 124362306a36Sopenharmony_ci elf->e_machine = ELF_ARCH; 124462306a36Sopenharmony_ci elf->e_version = EV_CURRENT; 124562306a36Sopenharmony_ci elf->e_entry = 0; 124662306a36Sopenharmony_ci elf->e_phoff = sizeof(struct elfhdr); 124762306a36Sopenharmony_ci elf->e_shoff = 0; 124862306a36Sopenharmony_ci elf->e_flags = ELF_FDPIC_CORE_EFLAGS; 124962306a36Sopenharmony_ci elf->e_ehsize = sizeof(struct elfhdr); 125062306a36Sopenharmony_ci elf->e_phentsize = sizeof(struct elf_phdr); 125162306a36Sopenharmony_ci elf->e_phnum = segs; 125262306a36Sopenharmony_ci elf->e_shentsize = 0; 125362306a36Sopenharmony_ci elf->e_shnum = 0; 125462306a36Sopenharmony_ci elf->e_shstrndx = 0; 125562306a36Sopenharmony_ci return; 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic inline void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci phdr->p_type = PT_NOTE; 126162306a36Sopenharmony_ci phdr->p_offset = offset; 126262306a36Sopenharmony_ci phdr->p_vaddr = 0; 126362306a36Sopenharmony_ci phdr->p_paddr = 0; 126462306a36Sopenharmony_ci phdr->p_filesz = sz; 126562306a36Sopenharmony_ci phdr->p_memsz = 0; 126662306a36Sopenharmony_ci phdr->p_flags = 0; 126762306a36Sopenharmony_ci phdr->p_align = 4; 126862306a36Sopenharmony_ci return; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic inline void fill_note(struct memelfnote *note, const char *name, int type, 127262306a36Sopenharmony_ci unsigned int sz, void *data) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci note->name = name; 127562306a36Sopenharmony_ci note->type = type; 127662306a36Sopenharmony_ci note->datasz = sz; 127762306a36Sopenharmony_ci note->data = data; 127862306a36Sopenharmony_ci return; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci/* 128262306a36Sopenharmony_ci * fill up all the fields in prstatus from the given task struct, except 128362306a36Sopenharmony_ci * registers which need to be filled up separately. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_cistatic void fill_prstatus(struct elf_prstatus_common *prstatus, 128662306a36Sopenharmony_ci struct task_struct *p, long signr) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; 128962306a36Sopenharmony_ci prstatus->pr_sigpend = p->pending.signal.sig[0]; 129062306a36Sopenharmony_ci prstatus->pr_sighold = p->blocked.sig[0]; 129162306a36Sopenharmony_ci rcu_read_lock(); 129262306a36Sopenharmony_ci prstatus->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent)); 129362306a36Sopenharmony_ci rcu_read_unlock(); 129462306a36Sopenharmony_ci prstatus->pr_pid = task_pid_vnr(p); 129562306a36Sopenharmony_ci prstatus->pr_pgrp = task_pgrp_vnr(p); 129662306a36Sopenharmony_ci prstatus->pr_sid = task_session_vnr(p); 129762306a36Sopenharmony_ci if (thread_group_leader(p)) { 129862306a36Sopenharmony_ci struct task_cputime cputime; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci * This is the record for the group leader. It shows the 130262306a36Sopenharmony_ci * group-wide total, not its individual thread total. 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ci thread_group_cputime(p, &cputime); 130562306a36Sopenharmony_ci prstatus->pr_utime = ns_to_kernel_old_timeval(cputime.utime); 130662306a36Sopenharmony_ci prstatus->pr_stime = ns_to_kernel_old_timeval(cputime.stime); 130762306a36Sopenharmony_ci } else { 130862306a36Sopenharmony_ci u64 utime, stime; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci task_cputime(p, &utime, &stime); 131162306a36Sopenharmony_ci prstatus->pr_utime = ns_to_kernel_old_timeval(utime); 131262306a36Sopenharmony_ci prstatus->pr_stime = ns_to_kernel_old_timeval(stime); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci prstatus->pr_cutime = ns_to_kernel_old_timeval(p->signal->cutime); 131562306a36Sopenharmony_ci prstatus->pr_cstime = ns_to_kernel_old_timeval(p->signal->cstime); 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, 131962306a36Sopenharmony_ci struct mm_struct *mm) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci const struct cred *cred; 132262306a36Sopenharmony_ci unsigned int i, len; 132362306a36Sopenharmony_ci unsigned int state; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* first copy the parameters from user space */ 132662306a36Sopenharmony_ci memset(psinfo, 0, sizeof(struct elf_prpsinfo)); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci len = mm->arg_end - mm->arg_start; 132962306a36Sopenharmony_ci if (len >= ELF_PRARGSZ) 133062306a36Sopenharmony_ci len = ELF_PRARGSZ - 1; 133162306a36Sopenharmony_ci if (copy_from_user(&psinfo->pr_psargs, 133262306a36Sopenharmony_ci (const char __user *) mm->arg_start, len)) 133362306a36Sopenharmony_ci return -EFAULT; 133462306a36Sopenharmony_ci for (i = 0; i < len; i++) 133562306a36Sopenharmony_ci if (psinfo->pr_psargs[i] == 0) 133662306a36Sopenharmony_ci psinfo->pr_psargs[i] = ' '; 133762306a36Sopenharmony_ci psinfo->pr_psargs[len] = 0; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci rcu_read_lock(); 134062306a36Sopenharmony_ci psinfo->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent)); 134162306a36Sopenharmony_ci rcu_read_unlock(); 134262306a36Sopenharmony_ci psinfo->pr_pid = task_pid_vnr(p); 134362306a36Sopenharmony_ci psinfo->pr_pgrp = task_pgrp_vnr(p); 134462306a36Sopenharmony_ci psinfo->pr_sid = task_session_vnr(p); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci state = READ_ONCE(p->__state); 134762306a36Sopenharmony_ci i = state ? ffz(~state) + 1 : 0; 134862306a36Sopenharmony_ci psinfo->pr_state = i; 134962306a36Sopenharmony_ci psinfo->pr_sname = (i > 5) ? '.' : "RSDTZW"[i]; 135062306a36Sopenharmony_ci psinfo->pr_zomb = psinfo->pr_sname == 'Z'; 135162306a36Sopenharmony_ci psinfo->pr_nice = task_nice(p); 135262306a36Sopenharmony_ci psinfo->pr_flag = p->flags; 135362306a36Sopenharmony_ci rcu_read_lock(); 135462306a36Sopenharmony_ci cred = __task_cred(p); 135562306a36Sopenharmony_ci SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid)); 135662306a36Sopenharmony_ci SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid)); 135762306a36Sopenharmony_ci rcu_read_unlock(); 135862306a36Sopenharmony_ci strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname)); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci/* Here is the structure in which status of each thread is captured. */ 136462306a36Sopenharmony_cistruct elf_thread_status 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci struct elf_thread_status *next; 136762306a36Sopenharmony_ci struct elf_prstatus_fdpic prstatus; /* NT_PRSTATUS */ 136862306a36Sopenharmony_ci elf_fpregset_t fpu; /* NT_PRFPREG */ 136962306a36Sopenharmony_ci struct memelfnote notes[2]; 137062306a36Sopenharmony_ci int num_notes; 137162306a36Sopenharmony_ci}; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci/* 137462306a36Sopenharmony_ci * In order to add the specific thread information for the elf file format, 137562306a36Sopenharmony_ci * we need to keep a linked list of every thread's pr_status and then create 137662306a36Sopenharmony_ci * a single section for them in the final core file. 137762306a36Sopenharmony_ci */ 137862306a36Sopenharmony_cistatic struct elf_thread_status *elf_dump_thread_status(long signr, struct task_struct *p, int *sz) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci const struct user_regset_view *view = task_user_regset_view(p); 138162306a36Sopenharmony_ci struct elf_thread_status *t; 138262306a36Sopenharmony_ci int i, ret; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci t = kzalloc(sizeof(struct elf_thread_status), GFP_KERNEL); 138562306a36Sopenharmony_ci if (!t) 138662306a36Sopenharmony_ci return t; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci fill_prstatus(&t->prstatus.common, p, signr); 138962306a36Sopenharmony_ci t->prstatus.pr_exec_fdpic_loadmap = p->mm->context.exec_fdpic_loadmap; 139062306a36Sopenharmony_ci t->prstatus.pr_interp_fdpic_loadmap = p->mm->context.interp_fdpic_loadmap; 139162306a36Sopenharmony_ci regset_get(p, &view->regsets[0], 139262306a36Sopenharmony_ci sizeof(t->prstatus.pr_reg), &t->prstatus.pr_reg); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus), 139562306a36Sopenharmony_ci &t->prstatus); 139662306a36Sopenharmony_ci t->num_notes++; 139762306a36Sopenharmony_ci *sz += notesize(&t->notes[0]); 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci for (i = 1; i < view->n; ++i) { 140062306a36Sopenharmony_ci const struct user_regset *regset = &view->regsets[i]; 140162306a36Sopenharmony_ci if (regset->core_note_type != NT_PRFPREG) 140262306a36Sopenharmony_ci continue; 140362306a36Sopenharmony_ci if (regset->active && regset->active(p, regset) <= 0) 140462306a36Sopenharmony_ci continue; 140562306a36Sopenharmony_ci ret = regset_get(p, regset, sizeof(t->fpu), &t->fpu); 140662306a36Sopenharmony_ci if (ret >= 0) 140762306a36Sopenharmony_ci t->prstatus.pr_fpvalid = 1; 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (t->prstatus.pr_fpvalid) { 141262306a36Sopenharmony_ci fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu), 141362306a36Sopenharmony_ci &t->fpu); 141462306a36Sopenharmony_ci t->num_notes++; 141562306a36Sopenharmony_ci *sz += notesize(&t->notes[1]); 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci return t; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum, 142162306a36Sopenharmony_ci elf_addr_t e_shoff, int segs) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci elf->e_shoff = e_shoff; 142462306a36Sopenharmony_ci elf->e_shentsize = sizeof(*shdr4extnum); 142562306a36Sopenharmony_ci elf->e_shnum = 1; 142662306a36Sopenharmony_ci elf->e_shstrndx = SHN_UNDEF; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci memset(shdr4extnum, 0, sizeof(*shdr4extnum)); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci shdr4extnum->sh_type = SHT_NULL; 143162306a36Sopenharmony_ci shdr4extnum->sh_size = elf->e_shnum; 143262306a36Sopenharmony_ci shdr4extnum->sh_link = elf->e_shstrndx; 143362306a36Sopenharmony_ci shdr4extnum->sh_info = segs; 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci/* 143762306a36Sopenharmony_ci * dump the segments for an MMU process 143862306a36Sopenharmony_ci */ 143962306a36Sopenharmony_cistatic bool elf_fdpic_dump_segments(struct coredump_params *cprm, 144062306a36Sopenharmony_ci struct core_vma_metadata *vma_meta, 144162306a36Sopenharmony_ci int vma_count) 144262306a36Sopenharmony_ci{ 144362306a36Sopenharmony_ci int i; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci for (i = 0; i < vma_count; i++) { 144662306a36Sopenharmony_ci struct core_vma_metadata *meta = vma_meta + i; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (!dump_user_range(cprm, meta->start, meta->dump_size)) 144962306a36Sopenharmony_ci return false; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci return true; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci/* 145562306a36Sopenharmony_ci * Actual dumper 145662306a36Sopenharmony_ci * 145762306a36Sopenharmony_ci * This is a two-pass process; first we find the offsets of the bits, 145862306a36Sopenharmony_ci * and then they are actually written out. If we run out of core limit 145962306a36Sopenharmony_ci * we just truncate. 146062306a36Sopenharmony_ci */ 146162306a36Sopenharmony_cistatic int elf_fdpic_core_dump(struct coredump_params *cprm) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci int has_dumped = 0; 146462306a36Sopenharmony_ci int segs; 146562306a36Sopenharmony_ci int i; 146662306a36Sopenharmony_ci struct elfhdr *elf = NULL; 146762306a36Sopenharmony_ci loff_t offset = 0, dataoff; 146862306a36Sopenharmony_ci struct memelfnote psinfo_note, auxv_note; 146962306a36Sopenharmony_ci struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ 147062306a36Sopenharmony_ci struct elf_thread_status *thread_list = NULL; 147162306a36Sopenharmony_ci int thread_status_size = 0; 147262306a36Sopenharmony_ci elf_addr_t *auxv; 147362306a36Sopenharmony_ci struct elf_phdr *phdr4note = NULL; 147462306a36Sopenharmony_ci struct elf_shdr *shdr4extnum = NULL; 147562306a36Sopenharmony_ci Elf_Half e_phnum; 147662306a36Sopenharmony_ci elf_addr_t e_shoff; 147762306a36Sopenharmony_ci struct core_thread *ct; 147862306a36Sopenharmony_ci struct elf_thread_status *tmp; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci /* alloc memory for large data structures: too large to be on stack */ 148162306a36Sopenharmony_ci elf = kmalloc(sizeof(*elf), GFP_KERNEL); 148262306a36Sopenharmony_ci if (!elf) 148362306a36Sopenharmony_ci goto end_coredump; 148462306a36Sopenharmony_ci psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); 148562306a36Sopenharmony_ci if (!psinfo) 148662306a36Sopenharmony_ci goto end_coredump; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci for (ct = current->signal->core_state->dumper.next; 148962306a36Sopenharmony_ci ct; ct = ct->next) { 149062306a36Sopenharmony_ci tmp = elf_dump_thread_status(cprm->siginfo->si_signo, 149162306a36Sopenharmony_ci ct->task, &thread_status_size); 149262306a36Sopenharmony_ci if (!tmp) 149362306a36Sopenharmony_ci goto end_coredump; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci tmp->next = thread_list; 149662306a36Sopenharmony_ci thread_list = tmp; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci /* now collect the dump for the current */ 150062306a36Sopenharmony_ci tmp = elf_dump_thread_status(cprm->siginfo->si_signo, 150162306a36Sopenharmony_ci current, &thread_status_size); 150262306a36Sopenharmony_ci if (!tmp) 150362306a36Sopenharmony_ci goto end_coredump; 150462306a36Sopenharmony_ci tmp->next = thread_list; 150562306a36Sopenharmony_ci thread_list = tmp; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci segs = cprm->vma_count + elf_core_extra_phdrs(cprm); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* for notes section */ 151062306a36Sopenharmony_ci segs++; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid 151362306a36Sopenharmony_ci * this, kernel supports extended numbering. Have a look at 151462306a36Sopenharmony_ci * include/linux/elf.h for further information. */ 151562306a36Sopenharmony_ci e_phnum = segs > PN_XNUM ? PN_XNUM : segs; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci /* Set up header */ 151862306a36Sopenharmony_ci fill_elf_fdpic_header(elf, e_phnum); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci has_dumped = 1; 152162306a36Sopenharmony_ci /* 152262306a36Sopenharmony_ci * Set up the notes in similar form to SVR4 core dumps made 152362306a36Sopenharmony_ci * with info from their /proc. 152462306a36Sopenharmony_ci */ 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci fill_psinfo(psinfo, current->group_leader, current->mm); 152762306a36Sopenharmony_ci fill_note(&psinfo_note, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); 152862306a36Sopenharmony_ci thread_status_size += notesize(&psinfo_note); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci auxv = (elf_addr_t *) current->mm->saved_auxv; 153162306a36Sopenharmony_ci i = 0; 153262306a36Sopenharmony_ci do 153362306a36Sopenharmony_ci i += 2; 153462306a36Sopenharmony_ci while (auxv[i - 2] != AT_NULL); 153562306a36Sopenharmony_ci fill_note(&auxv_note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); 153662306a36Sopenharmony_ci thread_status_size += notesize(&auxv_note); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci offset = sizeof(*elf); /* ELF header */ 153962306a36Sopenharmony_ci offset += segs * sizeof(struct elf_phdr); /* Program headers */ 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci /* Write notes phdr entry */ 154262306a36Sopenharmony_ci phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); 154362306a36Sopenharmony_ci if (!phdr4note) 154462306a36Sopenharmony_ci goto end_coredump; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci fill_elf_note_phdr(phdr4note, thread_status_size, offset); 154762306a36Sopenharmony_ci offset += thread_status_size; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* Page-align dumped data */ 155062306a36Sopenharmony_ci dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci offset += cprm->vma_data_size; 155362306a36Sopenharmony_ci offset += elf_core_extra_data_size(cprm); 155462306a36Sopenharmony_ci e_shoff = offset; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (e_phnum == PN_XNUM) { 155762306a36Sopenharmony_ci shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL); 155862306a36Sopenharmony_ci if (!shdr4extnum) 155962306a36Sopenharmony_ci goto end_coredump; 156062306a36Sopenharmony_ci fill_extnum_info(elf, shdr4extnum, e_shoff, segs); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci offset = dataoff; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (!dump_emit(cprm, elf, sizeof(*elf))) 156662306a36Sopenharmony_ci goto end_coredump; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (!dump_emit(cprm, phdr4note, sizeof(*phdr4note))) 156962306a36Sopenharmony_ci goto end_coredump; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* write program headers for segments dump */ 157262306a36Sopenharmony_ci for (i = 0; i < cprm->vma_count; i++) { 157362306a36Sopenharmony_ci struct core_vma_metadata *meta = cprm->vma_meta + i; 157462306a36Sopenharmony_ci struct elf_phdr phdr; 157562306a36Sopenharmony_ci size_t sz; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci sz = meta->end - meta->start; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci phdr.p_type = PT_LOAD; 158062306a36Sopenharmony_ci phdr.p_offset = offset; 158162306a36Sopenharmony_ci phdr.p_vaddr = meta->start; 158262306a36Sopenharmony_ci phdr.p_paddr = 0; 158362306a36Sopenharmony_ci phdr.p_filesz = meta->dump_size; 158462306a36Sopenharmony_ci phdr.p_memsz = sz; 158562306a36Sopenharmony_ci offset += phdr.p_filesz; 158662306a36Sopenharmony_ci phdr.p_flags = 0; 158762306a36Sopenharmony_ci if (meta->flags & VM_READ) 158862306a36Sopenharmony_ci phdr.p_flags |= PF_R; 158962306a36Sopenharmony_ci if (meta->flags & VM_WRITE) 159062306a36Sopenharmony_ci phdr.p_flags |= PF_W; 159162306a36Sopenharmony_ci if (meta->flags & VM_EXEC) 159262306a36Sopenharmony_ci phdr.p_flags |= PF_X; 159362306a36Sopenharmony_ci phdr.p_align = ELF_EXEC_PAGESIZE; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (!dump_emit(cprm, &phdr, sizeof(phdr))) 159662306a36Sopenharmony_ci goto end_coredump; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (!elf_core_write_extra_phdrs(cprm, offset)) 160062306a36Sopenharmony_ci goto end_coredump; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* write out the notes section */ 160362306a36Sopenharmony_ci if (!writenote(thread_list->notes, cprm)) 160462306a36Sopenharmony_ci goto end_coredump; 160562306a36Sopenharmony_ci if (!writenote(&psinfo_note, cprm)) 160662306a36Sopenharmony_ci goto end_coredump; 160762306a36Sopenharmony_ci if (!writenote(&auxv_note, cprm)) 160862306a36Sopenharmony_ci goto end_coredump; 160962306a36Sopenharmony_ci for (i = 1; i < thread_list->num_notes; i++) 161062306a36Sopenharmony_ci if (!writenote(thread_list->notes + i, cprm)) 161162306a36Sopenharmony_ci goto end_coredump; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci /* write out the thread status notes section */ 161462306a36Sopenharmony_ci for (tmp = thread_list->next; tmp; tmp = tmp->next) { 161562306a36Sopenharmony_ci for (i = 0; i < tmp->num_notes; i++) 161662306a36Sopenharmony_ci if (!writenote(&tmp->notes[i], cprm)) 161762306a36Sopenharmony_ci goto end_coredump; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci dump_skip_to(cprm, dataoff); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci if (!elf_fdpic_dump_segments(cprm, cprm->vma_meta, cprm->vma_count)) 162362306a36Sopenharmony_ci goto end_coredump; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (!elf_core_write_extra_data(cprm)) 162662306a36Sopenharmony_ci goto end_coredump; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci if (e_phnum == PN_XNUM) { 162962306a36Sopenharmony_ci if (!dump_emit(cprm, shdr4extnum, sizeof(*shdr4extnum))) 163062306a36Sopenharmony_ci goto end_coredump; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (cprm->file->f_pos != offset) { 163462306a36Sopenharmony_ci /* Sanity check */ 163562306a36Sopenharmony_ci printk(KERN_WARNING 163662306a36Sopenharmony_ci "elf_core_dump: file->f_pos (%lld) != offset (%lld)\n", 163762306a36Sopenharmony_ci cprm->file->f_pos, offset); 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ciend_coredump: 164162306a36Sopenharmony_ci while (thread_list) { 164262306a36Sopenharmony_ci tmp = thread_list; 164362306a36Sopenharmony_ci thread_list = thread_list->next; 164462306a36Sopenharmony_ci kfree(tmp); 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci kfree(phdr4note); 164762306a36Sopenharmony_ci kfree(elf); 164862306a36Sopenharmony_ci kfree(psinfo); 164962306a36Sopenharmony_ci kfree(shdr4extnum); 165062306a36Sopenharmony_ci return has_dumped; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci#endif /* CONFIG_ELF_CORE */ 1654