summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/elf.h16
-rw-r--r--arch/arm/include/asm/mmu.h8
-rw-r--r--arch/arm/include/asm/processor.h22
-rw-r--r--arch/arm/include/asm/ucontext.h1
-rw-r--r--arch/arm/include/uapi/asm/ptrace.h4
-rw-r--r--arch/arm/include/uapi/asm/unistd.h1
-rw-r--r--arch/arm/kernel/asm-offsets.c4
-rw-r--r--arch/arm/kernel/elf.c24
-rw-r--r--arch/arm/kernel/signal.c53
-rw-r--r--arch/arm/kernel/signal.h11
-rw-r--r--arch/arm/kernel/sigreturn_codes.S56
-rw-r--r--arch/arm/kernel/traps.c3
-rw-r--r--fs/Kconfig.binfmt4
-rw-r--r--fs/binfmt_elf.c15
-rw-r--r--fs/binfmt_elf_fdpic.c13
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;
}