Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 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.
  • Loading branch information
Russell King committed Oct 2, 2017
2 parents 4762424 + cdf3888 commit 1bb0783
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 36 deletions.
16 changes: 14 additions & 2 deletions arch/arm/include/asm/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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))

Expand Down
8 changes: 8 additions & 0 deletions arch/arm/include/asm/mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
22 changes: 15 additions & 7 deletions arch/arm/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions arch/arm/include/asm/ucontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions arch/arm/include/uapi/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/arm/include/uapi/asm/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
4 changes: 4 additions & 0 deletions arch/arm/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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));
Expand Down
24 changes: 23 additions & 1 deletion arch/arm/kernel/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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
53 changes: 38 additions & 15 deletions arch/arm/kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions arch/arm/kernel/signal.h
Original file line number Diff line number Diff line change
@@ -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;
};
56 changes: 51 additions & 5 deletions arch/arm/kernel/sigreturn_codes.S
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* GNU General Public License for more details.
*/

#include <asm/assembler.h>
#include <asm/asm-offsets.h>
#include <asm/unistd.h>

/*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions arch/arm/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions fs/Kconfig.binfmt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 1bb0783

Please sign in to comment.