Merge branch 'fdpic' of http://git.linaro.org/people/nicolas.pitre/linux into devel...
authorRussell King <rmk+kernel@armlinux.org.uk>
Thu, 28 Sep 2017 10:16:33 +0000 (11:16 +0100)
committerRussell King <rmk+kernel@armlinux.org.uk>
Mon, 2 Oct 2017 22:16:29 +0000 (23:16 +0100)
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.

15 files changed:
arch/arm/include/asm/elf.h
arch/arm/include/asm/mmu.h
arch/arm/include/asm/processor.h
arch/arm/include/asm/ucontext.h
arch/arm/include/uapi/asm/ptrace.h
arch/arm/include/uapi/asm/unistd.h
arch/arm/kernel/asm-offsets.c
arch/arm/kernel/elf.c
arch/arm/kernel/signal.c
arch/arm/kernel/signal.h [new file with mode: 0644]
arch/arm/kernel/sigreturn_codes.S
arch/arm/kernel/traps.c
fs/Kconfig.binfmt
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c

index f13ae15..ad0ca4f 100644 (file)
@@ -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))
 
index a5b4742..bdec37c 100644 (file)
@@ -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
index c3d5fc1..338cbe0 100644 (file)
@@ -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 */
index 921d827..b42c75a 100644 (file)
@@ -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
index 5af0ed1..3173eb9 100644 (file)
 #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
index 28bd456..575b25f 100644 (file)
@@ -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 */
index 6080082..13c1558 100644 (file)
@@ -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));
index d0d1e83..569e69e 100644 (file)
@@ -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
index e2de50b..2379734 100644 (file)
 #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 (file)
index 0000000..b7b838b
--- /dev/null
@@ -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;
+};
index b84d0cb..2c7b22e 100644 (file)
@@ -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
index 948c648..43c0560 100644 (file)
@@ -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
index b2f82cf..58c2bbd 100644 (file)
@@ -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
index 73b01e4..e4f7ef8 100644 (file)
 #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 */
 
index e70c039..5429b03 100644 (file)
@@ -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;
                        }