>From 68d2e0e68bed010d3dd083567d6d7f72e4bc2b52 Mon Sep 17 00:00:00 2001 From: Mickael Guene Date: Mon, 13 Jan 2014 15:48:29 +0100 Subject: [PATCH 1/5] fdpic: Add support for fdpic binaries loading Change-Id: Ia5947eb1b7d58b4338c67e6c8c9452d79bcf4504 Signed-off-by: Mickael Guene Conflicts: fs/binfmt_elf_fdpic.c --- arch/arm/include/asm/elf.h | 13 +++++++ arch/arm/include/asm/mmu.h | 4 +++ arch/arm/include/uapi/asm/ptrace.h | 5 +++ arch/arm/kernel/ptrace.c | 23 +++++++++++++ arch/arm/kernel/signal.c | 69 ++++++++++++++++++++++++++++++++++++-- fs/Kconfig.binfmt | 2 +- fs/binfmt_elf_fdpic.c | 18 +++++----- 7 files changed, 120 insertions(+), 14 deletions(-) diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index d2315ffd8f12..06b4c08de8d4 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -31,6 +31,7 @@ typedef struct user_fp elf_fpregset_t; #define EF_ARM_BE8 0x00800000 /* ABI 4,5 */ #define EF_ARM_LE8 0x00400000 /* ABI 4,5 */ +#define EF_ARM_FDPIC 0x00001000 #define EF_ARM_MAVERICK_FLOAT 0x00000800 /* ABI 0 */ #define EF_ARM_VFP_FLOAT 0x00000400 /* ABI 0 */ #define EF_ARM_SOFT_FLOAT 0x00000200 /* ABI 0 */ @@ -109,6 +110,10 @@ struct task_struct; int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); #define ELF_CORE_COPY_TASK_REGS dump_task_regs +#define elf_check_fdpic(x) ((x)->e_flags & EF_ARM_FDPIC) +#define elf_check_const_displacement(x) ((x)->e_flags & EF_ARM_PIC) + +#define ELF_FDPIC_CORE_EFLAGS EF_ARM_FDPIC #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096 @@ -124,6 +129,14 @@ int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); have no such handler. */ #define ELF_PLAT_INIT(_r, load_addr) (_r)->ARM_r0 = 0 +/* fdpic binary startup macro */ +#define ELF_FDPIC_PLAT_INIT(_r, _exec_map_addr, _interp_map_addr, dynamic_addr) \ +do { \ + (_r)->ARM_r7 = _exec_map_addr; \ + (_r)->ARM_r8 = _interp_map_addr; \ + (_r)->ARM_r9 = dynamic_addr; \ +} while(0) + extern void elf_set_personality(const struct elf32_hdr *); #define SET_PERSONALITY(ex) elf_set_personality(&(ex)) diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h index a5b47421059d..d24ff6f451f1 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h @@ -33,6 +33,10 @@ typedef struct { */ typedef struct { unsigned long end_brk; +#ifdef CONFIG_BINFMT_ELF_FDPIC + unsigned long exec_fdpic_loadmap; + unsigned long interp_fdpic_loadmap; +#endif } mm_context_t; #endif diff --git a/arch/arm/include/uapi/asm/ptrace.h b/arch/arm/include/uapi/asm/ptrace.h index 5af0ed1b825a..2a655fc9a38e 100644 --- a/arch/arm/include/uapi/asm/ptrace.h +++ b/arch/arm/include/uapi/asm/ptrace.h @@ -32,6 +32,11 @@ #define PTRACE_GETHBPREGS 29 #define PTRACE_SETHBPREGS 30 +#define PTRACE_GETFDPIC 31 /* get the ELF fdpic loadmap address */ + +#define PTRACE_GETFDPIC_EXEC 0 /* [addr] request the executable loadmap */ +#define PTRACE_GETFDPIC_INTERP 1 /* [addr] request the interpreter loadmap */ + /* * PSR bits * Note on V7M there is no mode contained in the PSR diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index ef9119f7462e..427786bc7e45 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -894,6 +894,29 @@ long arch_ptrace(struct task_struct *child, long request, (unsigned long __user *)data); break; #endif +#ifdef CONFIG_BINFMT_ELF_FDPIC + case PTRACE_GETFDPIC: { + unsigned long tmp = 0; + + switch (addr) { + case PTRACE_GETFDPIC_EXEC: + tmp = child->mm->context.exec_fdpic_loadmap; + break; + case PTRACE_GETFDPIC_INTERP: + tmp = child->mm->context.interp_fdpic_loadmap; + break; + default: + break; + } + + ret = 0; + if (put_user(tmp, (unsigned long __user *)data)) { + ret = -EFAULT; + break; + } + break; + } +#endif default: ret = ptrace_request(child, request, addr, data); diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7b8f2141427b..e5d97e0d0cfb 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -24,6 +24,41 @@ extern const unsigned long sigreturn_codes[7]; +struct fdpic_func_descriptor { + unsigned long text; + unsigned long GOT; +}; + +/* + * For ARM syscalls, we encode the syscall number into the instruction. + */ +#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) +#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) + +/* + * With EABI, the syscall number has to be loaded into r7. + */ +#define MOV_R7_NR_SIGRETURN (0xe3a07000 | (__NR_sigreturn - __NR_SYSCALL_BASE)) +#define MOV_R7_NR_RT_SIGRETURN (0xe3a07000 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) + +const unsigned long sigreturn_fdpic_codes[3] = { + 0xe59fc004, /* ldr r12, [pc, #4] to read function descriptor */ + 0xe59c9004, /* ldr r9, [r12, #4] to setup got */ + 0xe59cf000 /* ldr pc, [r12] to jump into restorer */ +}; + +/* + * For Thumb syscalls, we pass the syscall number via r7. We therefore + * need two 16-bit instructions. + */ +#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE)) +#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) + +static const unsigned long sigreturn_codes[7] = { + MOV_R7_NR_SIGRETURN, SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, + MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, +}; + static unsigned long signal_return_offset; #ifdef CONFIG_CRUNCH @@ -131,7 +166,7 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) */ struct sigframe { struct ucontext uc; - unsigned long retcode[2]; + unsigned long retcode[4]; }; struct rt_sigframe { @@ -322,10 +357,18 @@ static int setup_return(struct pt_regs *regs, struct ksignal *ksig, unsigned long __user *rc, void __user *frame) { - unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler; + unsigned long handler; unsigned long retcode; int thumb = 0; unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT); + unsigned long r9 = 0; + + if (current->personality & FDPIC_FUNCPTRS) { + struct fdpic_func_descriptor __user *funcptr = (struct fdpic_func_descriptor __user *)ksig->ka.sa.sa_handler; + __get_user(handler, &funcptr->text); + __get_user(r9, &funcptr->GOT); + } else + handler = (unsigned long)ksig->ka.sa.sa_handler; cpsr |= PSR_ENDSTATE; @@ -364,7 +407,25 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, #endif if (ksig->ka.sa.sa_flags & SA_RESTORER) { - retcode = (unsigned long)ksig->ka.sa.sa_restorer; + if (current->personality & FDPIC_FUNCPTRS) { + /* for fdpic we ensure that restorer is call with a correct r9 value + * for that we need to write code on stack that setup r9 and jump back to restorer value + */ + struct fdpic_func_descriptor __user *funcptr = (struct fdpic_func_descriptor __user *)ksig->ka.sa.sa_restorer; + + if (__put_user(sigreturn_fdpic_codes[0], rc) || + __put_user(sigreturn_fdpic_codes[1], rc+1) || + __put_user(sigreturn_fdpic_codes[2], rc+2) || + __put_user((unsigned long)funcptr, rc+3)) + return 1; + + /* last word of rc is data and so we don't need to invalidate icache for it */ + flush_icache_range((unsigned long)rc, (unsigned long)(rc + 3)); + + retcode = (unsigned long)rc; + } else { + retcode = (unsigned long)ksig->ka.sa.sa_restorer; + } } else { unsigned int idx = thumb << 1; @@ -409,6 +470,8 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, regs->ARM_lr = retcode; regs->ARM_pc = handler; regs->ARM_cpsr = cpsr; + if (current->personality & FDPIC_FUNCPTRS) + regs->ARM_r9 = r9; return 0; } diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index 2d0cbbd14cfc..9ff41609b02f 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -33,7 +33,7 @@ config ARCH_BINFMT_ELF_STATE config BINFMT_ELF_FDPIC bool "Kernel support for FDPIC ELF binaries" default y - depends on (FRV || BLACKFIN || (SUPERH32 && !MMU) || C6X) + depends on ((ARM && !MMU) || FRV || BLACKFIN || (SUPERH32 && !MMU) || C6X) help ELF FDPIC binaries are based on ELF, but allow the individual load segments of a binary to be located in memory independently of each diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index b1adb92e69de..583cdf14279e 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -345,12 +345,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) goto error; /* there's now no turning back... the old userspace image is dead, - * defunct, deceased, etc. - */ - if (elf_check_fdpic(&exec_params.hdr)) - set_personality(PER_LINUX_FDPIC); - else - set_personality(PER_LINUX); + * defunct, deceased, etc. after this point we have to exit via + * error_kill */ + /* TODO : call SET_PERSONALITY is certainly better .... */ + set_personality(PER_LINUX_FDPIC | PER_LINUX_32BIT); if (elf_read_implies_exec(&exec_params.hdr, executable_stack)) current->personality |= READ_IMPLIES_EXEC; @@ -444,6 +442,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) kdebug("- brk %lx", current->mm->brk); kdebug("- start_stack %lx", current->mm->start_stack); + /* everything is now ready... get the userspace context ready to roll */ + entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; + start_thread(regs, entryaddr, current->mm->start_stack); + #ifdef ELF_FDPIC_PLAT_INIT /* * The ABI may specify that certain registers be set up in special @@ -456,10 +458,6 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) dynaddr); #endif - /* everything is now ready... get the userspace context ready to roll */ - entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; - start_thread(regs, entryaddr, current->mm->start_stack); - retval = 0; error: -- 1.9.1