diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2017-09-28 11:16:33 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@armlinux.org.uk> | 2017-10-02 23:16:29 +0100 |
commit | 1bb078330b4f0836b5b12365cd8bf68d5f3bbff8 (patch) | |
tree | 0a15f41ace12c4b308ef6cc57e5aa3be0019e05d | |
parent | 476242482bdee72b30f188c2e025f0fa5df0208c (diff) | |
parent | cdf38888ed30523b1a6ee199449a86dbd1d864ce (diff) | |
download | linux-1bb078330b4f0836b5b12365cd8bf68d5f3bbff8.tar.bz2 |
Merge branch 'fdpic' of http://git.linaro.org/people/nicolas.pitre/linux into devel-stable
This series provides the needed changes to suport the ELF_FDPIC binary
format on ARM. Both MMU and non-MMU systems are supported. This format
has many advantages over the BFLT format used on MMU-less systems, such
as being real ELF that can be parsed by standard tools, can support
shared dynamic libs, etc.
-rw-r--r-- | arch/arm/include/asm/elf.h | 16 | ||||
-rw-r--r-- | arch/arm/include/asm/mmu.h | 8 | ||||
-rw-r--r-- | arch/arm/include/asm/processor.h | 22 | ||||
-rw-r--r-- | arch/arm/include/asm/ucontext.h | 1 | ||||
-rw-r--r-- | arch/arm/include/uapi/asm/ptrace.h | 4 | ||||
-rw-r--r-- | arch/arm/include/uapi/asm/unistd.h | 1 | ||||
-rw-r--r-- | arch/arm/kernel/asm-offsets.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/elf.c | 24 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 53 | ||||
-rw-r--r-- | arch/arm/kernel/signal.h | 11 | ||||
-rw-r--r-- | arch/arm/kernel/sigreturn_codes.S | 56 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 3 | ||||
-rw-r--r-- | fs/Kconfig.binfmt | 4 | ||||
-rw-r--r-- | fs/binfmt_elf.c | 15 | ||||
-rw-r--r-- | fs/binfmt_elf_fdpic.c | 13 |
15 files changed, 199 insertions, 36 deletions
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index f13ae153fb24..ad0ca4f2ba13 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -100,10 +100,15 @@ struct elf32_hdr; extern int elf_check_arch(const struct elf32_hdr *); #define elf_check_arch elf_check_arch +#define ELFOSABI_ARM_FDPIC 65 /* ARM FDPIC platform */ +#define elf_check_fdpic(x) ((x)->e_ident[EI_OSABI] == ELFOSABI_ARM_FDPIC) +#define elf_check_const_displacement(x) ((x)->e_flags & EF_ARM_PIC) +#define ELF_FDPIC_CORE_EFLAGS 0 + #define vmcore_elf64_check_arch(x) (0) -extern int arm_elf_read_implies_exec(const struct elf32_hdr *, int); -#define elf_read_implies_exec(ex,stk) arm_elf_read_implies_exec(&(ex), stk) +extern int arm_elf_read_implies_exec(int); +#define elf_read_implies_exec(ex,stk) arm_elf_read_implies_exec(stk) struct task_struct; int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); @@ -120,6 +125,13 @@ 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 +#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..bdec37c6ac35 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h @@ -14,6 +14,10 @@ typedef struct { #ifdef CONFIG_VDSO unsigned long vdso; #endif +#ifdef CONFIG_BINFMT_ELF_FDPIC + unsigned long exec_fdpic_loadmap; + unsigned long interp_fdpic_loadmap; +#endif } mm_context_t; #ifdef CONFIG_CPU_HAS_ASID @@ -33,6 +37,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/asm/processor.h b/arch/arm/include/asm/processor.h index c3d5fc124a05..338cbe0a18ef 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -47,15 +47,24 @@ struct thread_struct { #define INIT_THREAD { } -#ifdef CONFIG_MMU -#define nommu_start_thread(regs) do { } while (0) -#else -#define nommu_start_thread(regs) regs->ARM_r10 = current->mm->start_data -#endif - #define start_thread(regs,pc,sp) \ ({ \ + unsigned long r7, r8, r9; \ + \ + if (IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC)) { \ + r7 = regs->ARM_r7; \ + r8 = regs->ARM_r8; \ + r9 = regs->ARM_r9; \ + } \ memset(regs->uregs, 0, sizeof(regs->uregs)); \ + if (IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) && \ + current->personality & FDPIC_FUNCPTRS) { \ + regs->ARM_r7 = r7; \ + regs->ARM_r8 = r8; \ + regs->ARM_r9 = r9; \ + regs->ARM_r10 = current->mm->start_data; \ + } else if (!IS_ENABLED(CONFIG_MMU)) \ + regs->ARM_r10 = current->mm->start_data; \ if (current->personality & ADDR_LIMIT_32BIT) \ regs->ARM_cpsr = USR_MODE; \ else \ @@ -65,7 +74,6 @@ struct thread_struct { regs->ARM_cpsr |= PSR_ENDSTATE; \ regs->ARM_pc = pc & ~1; /* pc */ \ regs->ARM_sp = sp; /* sp */ \ - nommu_start_thread(regs); \ }) /* Forward declaration, a strange C thing */ diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h index 921d8274855c..b42c75ae0d19 100644 --- a/arch/arm/include/asm/ucontext.h +++ b/arch/arm/include/asm/ucontext.h @@ -2,6 +2,7 @@ #define _ASMARM_UCONTEXT_H #include <asm/fpstate.h> +#include <asm/user.h> /* * struct sigcontext only has room for the basic registers, but struct diff --git a/arch/arm/include/uapi/asm/ptrace.h b/arch/arm/include/uapi/asm/ptrace.h index 5af0ed1b825a..3173eb9751fd 100644 --- a/arch/arm/include/uapi/asm/ptrace.h +++ b/arch/arm/include/uapi/asm/ptrace.h @@ -31,6 +31,10 @@ #define PTRACE_SETVFPREGS 28 #define PTRACE_GETHBPREGS 29 #define PTRACE_SETHBPREGS 30 +#define PTRACE_GETFDPIC 31 + +#define PTRACE_GETFDPIC_EXEC 0 +#define PTRACE_GETFDPIC_INTERP 1 /* * PSR bits diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h index 28bd456494a3..575b25fc29c6 100644 --- a/arch/arm/include/uapi/asm/unistd.h +++ b/arch/arm/include/uapi/asm/unistd.h @@ -35,5 +35,6 @@ #define __ARM_NR_usr26 (__ARM_NR_BASE+3) #define __ARM_NR_usr32 (__ARM_NR_BASE+4) #define __ARM_NR_set_tls (__ARM_NR_BASE+5) +#define __ARM_NR_get_tls (__ARM_NR_BASE+6) #endif /* _UAPI__ASM_ARM_UNISTD_H */ diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 608008229c7d..13c155850822 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -28,6 +28,7 @@ #include <asm/vdso_datapage.h> #include <asm/hardware/cache-l2x0.h> #include <linux/kbuild.h> +#include "signal.h" /* * Make sure that the compiler and target are compatible. @@ -112,6 +113,9 @@ int main(void) DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit)); DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs)); BLANK(); + DEFINE(SIGFRAME_RC3_OFFSET, offsetof(struct sigframe, retcode[3])); + DEFINE(RT_SIGFRAME_RC3_OFFSET, offsetof(struct rt_sigframe, sig.retcode[3])); + BLANK(); #ifdef CONFIG_CACHE_L2X0 DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base)); DEFINE(L2X0_R_AUX_CTRL, offsetof(struct l2x0_regs, aux_ctrl)); diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c index d0d1e83150c9..569e69ece5ca 100644 --- a/arch/arm/kernel/elf.c +++ b/arch/arm/kernel/elf.c @@ -3,6 +3,7 @@ #include <linux/personality.h> #include <linux/binfmts.h> #include <linux/elf.h> +#include <linux/elf-fdpic.h> #include <asm/system_info.h> int elf_check_arch(const struct elf32_hdr *x) @@ -80,7 +81,7 @@ EXPORT_SYMBOL(elf_set_personality); * - the binary requires an executable stack * - we're running on a CPU which doesn't support NX. */ -int arm_elf_read_implies_exec(const struct elf32_hdr *x, int executable_stack) +int arm_elf_read_implies_exec(int executable_stack) { if (executable_stack != EXSTACK_DISABLE_X) return 1; @@ -89,3 +90,24 @@ int arm_elf_read_implies_exec(const struct elf32_hdr *x, int executable_stack) return 0; } EXPORT_SYMBOL(arm_elf_read_implies_exec); + +#if defined(CONFIG_MMU) && defined(CONFIG_BINFMT_ELF_FDPIC) + +void elf_fdpic_arch_lay_out_mm(struct elf_fdpic_params *exec_params, + struct elf_fdpic_params *interp_params, + unsigned long *start_stack, + unsigned long *start_brk) +{ + elf_set_personality(&exec_params->hdr); + + exec_params->load_addr = 0x8000; + interp_params->load_addr = ELF_ET_DYN_BASE; + *start_stack = TASK_SIZE - SZ_16M; + + if ((exec_params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == ELF_FDPIC_FLAG_INDEPENDENT) { + exec_params->flags &= ~ELF_FDPIC_FLAG_ARRANGEMENT; + exec_params->flags |= ELF_FDPIC_FLAG_CONSTDISP; + } +} + +#endif diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index e2de50bf8742..237973492479 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -19,11 +19,12 @@ #include <asm/elf.h> #include <asm/cacheflush.h> #include <asm/traps.h> -#include <asm/ucontext.h> #include <asm/unistd.h> #include <asm/vfp.h> -extern const unsigned long sigreturn_codes[7]; +#include "signal.h" + +extern const unsigned long sigreturn_codes[17]; static unsigned long signal_return_offset; @@ -172,15 +173,6 @@ static int restore_vfp_context(char __user **auxp) /* * Do a signal return; undo the signal stack. These are aligned to 64-bit. */ -struct sigframe { - struct ucontext uc; - unsigned long retcode[2]; -}; - -struct rt_sigframe { - struct siginfo info; - struct sigframe sig; -}; static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) { @@ -366,9 +358,20 @@ 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_fdpic_GOT = 0; unsigned long retcode; - int thumb = 0; + unsigned int idx, thumb = 0; unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT); + bool fdpic = IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) && + (current->personality & FDPIC_FUNCPTRS); + + if (fdpic) { + unsigned long __user *fdpic_func_desc = + (unsigned long __user *)handler; + if (__get_user(handler, &fdpic_func_desc[0]) || + __get_user(handler_fdpic_GOT, &fdpic_func_desc[1])) + return 1; + } cpsr |= PSR_ENDSTATE; @@ -408,9 +411,26 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, if (ksig->ka.sa.sa_flags & SA_RESTORER) { retcode = (unsigned long)ksig->ka.sa.sa_restorer; + if (fdpic) { + /* + * We need code to load the function descriptor. + * That code follows the standard sigreturn code + * (6 words), and is made of 3 + 2 words for each + * variant. The 4th copied word is the actual FD + * address that the assembly code expects. + */ + idx = 6 + thumb * 3; + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + idx += 5; + if (__put_user(sigreturn_codes[idx], rc ) || + __put_user(sigreturn_codes[idx+1], rc+1) || + __put_user(sigreturn_codes[idx+2], rc+2) || + __put_user(retcode, rc+3)) + return 1; + goto rc_finish; + } } else { - unsigned int idx = thumb << 1; - + idx = thumb << 1; if (ksig->ka.sa.sa_flags & SA_SIGINFO) idx += 3; @@ -422,6 +442,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, __put_user(sigreturn_codes[idx+1], rc+1)) return 1; +rc_finish: #ifdef CONFIG_MMU if (cpsr & MODE32_BIT) { struct mm_struct *mm = current->mm; @@ -441,7 +462,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, * the return code written onto the stack. */ flush_icache_range((unsigned long)rc, - (unsigned long)(rc + 2)); + (unsigned long)(rc + 3)); retcode = ((unsigned long)rc) + thumb; } @@ -451,6 +472,8 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, regs->ARM_sp = (unsigned long)frame; regs->ARM_lr = retcode; regs->ARM_pc = handler; + if (fdpic) + regs->ARM_r9 = handler_fdpic_GOT; regs->ARM_cpsr = cpsr; return 0; diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h new file mode 100644 index 000000000000..b7b838b05229 --- /dev/null +++ b/arch/arm/kernel/signal.h @@ -0,0 +1,11 @@ +#include <asm/ucontext.h> + +struct sigframe { + struct ucontext uc; + unsigned long retcode[4]; +}; + +struct rt_sigframe { + struct siginfo info; + struct sigframe sig; +}; diff --git a/arch/arm/kernel/sigreturn_codes.S b/arch/arm/kernel/sigreturn_codes.S index b84d0cb13682..2c7b22e32152 100644 --- a/arch/arm/kernel/sigreturn_codes.S +++ b/arch/arm/kernel/sigreturn_codes.S @@ -14,6 +14,8 @@ * GNU General Public License for more details. */ +#include <asm/assembler.h> +#include <asm/asm-offsets.h> #include <asm/unistd.h> /* @@ -51,6 +53,17 @@ ARM_OK( .arm ) .thumb .endm + .macro arm_fdpic_slot n + .org sigreturn_codes + 24 + 20 * (\n) +ARM_OK( .arm ) + .endm + + .macro thumb_fdpic_slot n + .org sigreturn_codes + 24 + 20 * (\n) + 12 + .thumb + .endm + + #if __LINUX_ARM_ARCH__ <= 4 /* * Note we manually set minimally required arch that supports @@ -90,13 +103,46 @@ ARM_OK( swi #(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE) ) movs r7, #(__NR_rt_sigreturn - __NR_SYSCALL_BASE) swi #0 + /* ARM sigreturn restorer FDPIC bounce code snippet */ + arm_fdpic_slot 0 +ARM_OK( ldr r3, [sp, #SIGFRAME_RC3_OFFSET] ) +ARM_OK( ldmia r3, {r3, r9} ) +#ifdef CONFIG_ARM_THUMB +ARM_OK( bx r3 ) +#else +ARM_OK( ret r3 ) +#endif + + /* Thumb sigreturn restorer FDPIC bounce code snippet */ + thumb_fdpic_slot 0 + ldr r3, [sp, #SIGFRAME_RC3_OFFSET] + ldmia r3, {r2, r3} + mov r9, r3 + bx r2 + + /* ARM sigreturn_rt restorer FDPIC bounce code snippet */ + arm_fdpic_slot 1 +ARM_OK( ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET] ) +ARM_OK( ldmia r3, {r3, r9} ) +#ifdef CONFIG_ARM_THUMB +ARM_OK( bx r3 ) +#else +ARM_OK( ret r3 ) +#endif + + /* Thumb sigreturn_rt restorer FDPIC bounce code snippet */ + thumb_fdpic_slot 1 + ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET] + ldmia r3, {r2, r3} + mov r9, r3 + bx r2 + /* - * Note on addtional space: setup_return in signal.c - * algorithm uses two words copy regardless whether - * it is thumb case or not, so we need additional - * word after real last entry. + * Note on additional space: setup_return in signal.c + * always copies the same number of words regardless whether + * it is thumb case or not, so we need one additional padding + * word after the last entry. */ - arm_slot 2 .space 4 .size sigreturn_codes, . - sigreturn_codes diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 948c648fea00..43c0560f7b2d 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -647,6 +647,9 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) set_tls(regs->ARM_r0); return 0; + case NR(get_tls): + return current_thread_info()->tp_value[0]; + default: /* Calls 9f00xx..9f07ff are defined to return -ENOSYS if not implemented, rather than raising SIGILL. This diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index b2f82cf6bf86..58c2bbd385ad 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -34,8 +34,8 @@ 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) + default y if !BINFMT_ELF + depends on (ARM || FRV || BLACKFIN || (SUPERH32 && !MMU) || C6X) select ELFCORE help ELF FDPIC binaries are based on ELF, but allow the individual load diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 73b01e474fdc..e4f7ef8294ac 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -51,6 +51,11 @@ #define user_siginfo_t siginfo_t #endif +/* That's for binfmt_elf_fdpic to deal with */ +#ifndef elf_check_fdpic +#define elf_check_fdpic(ex) false +#endif + static int load_elf_binary(struct linux_binprm *bprm); static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *, int, int, unsigned long); @@ -541,7 +546,8 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, if (interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) goto out; - if (!elf_check_arch(interp_elf_ex)) + if (!elf_check_arch(interp_elf_ex) || + elf_check_fdpic(interp_elf_ex)) goto out; if (!interpreter->f_op->mmap) goto out; @@ -718,6 +724,8 @@ static int load_elf_binary(struct linux_binprm *bprm) goto out; if (!elf_check_arch(&loc->elf_ex)) goto out; + if (elf_check_fdpic(&loc->elf_ex)) + goto out; if (!bprm->file->f_op->mmap) goto out; @@ -817,7 +825,8 @@ static int load_elf_binary(struct linux_binprm *bprm) if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0) goto out_free_dentry; /* Verify the interpreter has a valid arch */ - if (!elf_check_arch(&loc->interp_elf_ex)) + if (!elf_check_arch(&loc->interp_elf_ex) || + elf_check_fdpic(&loc->interp_elf_ex)) goto out_free_dentry; /* Load the interpreter program headers */ @@ -1190,6 +1199,8 @@ static int load_elf_library(struct file *file) if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || !elf_check_arch(&elf_ex) || !file->f_op->mmap) goto out; + if (elf_check_fdpic(&elf_ex)) + goto out; /* Now read in all of the header information */ diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index e70c039ac190..5429b035e249 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -378,6 +378,11 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) executable_stack); if (retval < 0) goto error; +#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES + retval = arch_setup_additional_pages(bprm, !!interpreter_name); + if (retval < 0) + goto error; +#endif #endif /* load the executable and interpreter into memory */ @@ -831,6 +836,9 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params, if (phdr->p_vaddr >= seg->p_vaddr && phdr->p_vaddr + phdr->p_memsz <= seg->p_vaddr + seg->p_memsz) { + Elf32_Dyn __user *dyn; + Elf32_Sword d_tag; + params->dynamic_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr; @@ -843,8 +851,9 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params, goto dynamic_error; tmp = phdr->p_memsz / sizeof(Elf32_Dyn); - if (((Elf32_Dyn *) - params->dynamic_addr)[tmp - 1].d_tag != 0) + dyn = (Elf32_Dyn __user *)params->dynamic_addr; + __get_user(d_tag, &dyn[tmp - 1].d_tag); + if (d_tag != 0) goto dynamic_error; break; } |