linux(4): Implement signal trampoline for arm64 in a FreeBSD-way

The implemenation differs from others Linuxulators.
For unwinders Linux ucontext_t is stored, however native machine context
is used to store/restore process state to avoid code duplication.

As DWARF Aarch64 does not define a register number for PC and provides no
direct way to encode the PC of the previous frame, CFI cannot describe a
signal trampoline frame. So, modified the vdso linker script to discard
unused sections.

Extensions are not implemented.

MFC after:		2 weeks
This commit is contained in:
Dmitry Chagin 2022-05-15 21:10:50 +03:00
parent 08e201a3b4
commit c56480a832
5 changed files with 152 additions and 54 deletions

View File

@ -164,7 +164,7 @@ struct l_newstat {
#define LINUX_SIG_SETMASK 2
/* sigaltstack */
#define LINUX_MINSIGSTKSZ 2048 /* XXX */
#define LINUX_MINSIGSTKSZ 5664 /* sigframe */
typedef void (*l_handler_t)(l_int);

View File

@ -3,6 +3,7 @@
*
* Copyright (C) 2018 Turing Robotic Industries Inc.
* Copyright (C) 2020 Andrew Turner <andrew@FreeBSD.org>
* Copyright (C) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -29,7 +30,7 @@
*/
/*
* arm64 Linux VDSO implementation.
* arm64 Linux VDSO signal trampoline.
*/
#include <machine/asm.h>
@ -45,8 +46,14 @@ linux_platform:
.text
nop /* This is what Linux calls a "Mysterious NOP". */
ENTRY(__kernel_rt_sigreturn)
EENTRY(__kernel_rt_sigreturn)
mov x8, #LINUX_SYS_linux_rt_sigreturn
svc #0
ret
END(__kernel_rt_sigreturn)
EEND(__kernel_rt_sigreturn)
EENTRY(linux_vdso_sigcode)
blr x8
mov x8, #LINUX_SYS_linux_rt_sigreturn
svc #0
EEND(linux_vdso_sigcode)

View File

@ -1,7 +1,7 @@
/*-
* Copyright (c) 1994-1996 Søren Schmidt
* Copyright (c) 2013 Dmitry Chagin <dchagin@FreeBSD.org>
* Copyright (c) 2018 Turing Robotic Industries Inc.
* Copyright (c) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -23,22 +23,62 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
*
* $FreeBSD$
*/
#ifndef _ARM64_LINUX_SIGFRAME_H_
#define _ARM64_LINUX_SIGFRAME_H_
/*
* This structure is different from the one used by Linux,
* but it doesn't matter - it's not user-accessible. We need
* it instead of the native one because of l_siginfo.
*/
struct _l_aarch64_ctx {
uint32_t magic;
uint32_t size;
};
#define L_FPSIMD_MAGIC 0x46508001
#define L_ESR_MAGIC 0x45535201
struct l_fpsimd_context {
struct _l_aarch64_ctx head;
uint32_t fpsr;
uint32_t fpcr;
__uint128_t vregs[32];
};
struct l_esr_context {
struct _l_aarch64_ctx head;
uint64_t esr;
};
struct l_sigcontext {
uint64_t fault_address;
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
uint64_t pstate;
uint8_t __reserved[4096] __attribute__((__aligned__(16)));
};
struct l_ucontext {
unsigned long uc_flags;
struct l_ucontext *uc_link;
l_stack_t uc_stack;
l_sigset_t uc_sigmask;
uint8_t __glibc_hole[1024 / 8 - sizeof(l_sigset_t)];
struct l_sigcontext uc_sc;
};
struct l_rt_sigframe {
l_siginfo_t sf_si;
struct l_ucontext sf_uc;
} __attribute__((__aligned__(16)));
struct l_sigframe {
struct l_siginfo sf_si;
ucontext_t sf_uc;
struct l_rt_sigframe sf;
/* frame_record */
uint64_t fp;
uint64_t lr;
ucontext_t uc;
};
#endif /* _ARM64_LINUX_SIGFRAME_H_ */

View File

@ -128,7 +128,7 @@ LIN_SDT_PROBE_DEFINE0(sysvec, linux_elf_fixup, todo);
LINUX_VDSO_SYM_CHAR(linux_platform);
LINUX_VDSO_SYM_INTPTR(kern_timekeep_base);
LINUX_VDSO_SYM_INTPTR(__kernel_rt_sigreturn);
LINUX_VDSO_SYM_INTPTR(linux_vdso_sigcode);
/* LINUXTODO: do we have traps to translate? */
static int
@ -405,21 +405,23 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp,
int
linux_rt_sigreturn(struct thread *td, struct linux_rt_sigreturn_args *args)
{
struct l_sigframe frame;
struct l_sigframe *frame;
ucontext_t uc;
struct trapframe *tf;
int error;
tf = td->td_frame;
frame = (struct l_sigframe *)tf->tf_sp;
if (copyin((void *)tf->tf_sp, &frame, sizeof(frame)))
if (copyin((void *)&frame->uc, &uc, sizeof(uc)))
return (EFAULT);
error = set_mcontext(td, &frame.sf_uc.uc_mcontext);
error = set_mcontext(td, &uc.uc_mcontext);
if (error != 0)
return (error);
/* Restore signal mask. */
kern_sigprocmask(td, SIG_SETMASK, &frame.sf_uc.uc_sigmask, NULL, 0);
kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0);
return (EJUSTRETURN);
}
@ -430,7 +432,12 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
struct thread *td;
struct proc *p;
struct trapframe *tf;
struct l_sigframe *fp, frame;
struct l_sigframe *fp, *frame;
struct l_fpsimd_context *fpsimd;
struct l_esr_context *esr;
l_stack_t uc_stack;
ucontext_t uc;
uint8_t *scr;
struct sigacts *psp;
int onstack, sig;
@ -464,36 +471,77 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
fp--;
fp = (struct l_sigframe *)STACKALIGN(fp);
/* Fill in the frame to copy out */
bzero(&frame, sizeof(frame));
get_mcontext(td, &frame.sf_uc.uc_mcontext, 0);
get_mcontext(td, &uc.uc_mcontext, 0);
uc.uc_sigmask = *mask;
/* Translate the signal. */
sig = bsd_to_linux_signal(sig);
siginfo_to_lsiginfo(&ksi->ksi_info, &frame.sf_si, sig);
frame.sf_uc.uc_sigmask = *mask;
frame.sf_uc.uc_stack = td->td_sigstk;
frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ?
(onstack ? SS_ONSTACK : 0) : SS_DISABLE;
uc_stack.ss_sp = PTROUT(td->td_sigstk.ss_sp);
uc_stack.ss_size = td->td_sigstk.ss_size;
uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ?
(onstack ? LINUX_SS_ONSTACK : 0) : LINUX_SS_DISABLE;
mtx_unlock(&psp->ps_mtx);
PROC_UNLOCK(td->td_proc);
/* Fill in the frame to copy out */
frame = malloc(sizeof(*frame), M_LINUX, M_WAITOK | M_ZERO);
memcpy(&frame->sf.sf_uc.uc_sc.regs, tf->tf_x, sizeof(tf->tf_x));
frame->sf.sf_uc.uc_sc.regs[30] = tf->tf_lr;
frame->sf.sf_uc.uc_sc.sp = tf->tf_sp;
frame->sf.sf_uc.uc_sc.pc = tf->tf_lr;
frame->sf.sf_uc.uc_sc.pstate = tf->tf_spsr;
frame->sf.sf_uc.uc_sc.fault_address = (register_t)ksi->ksi_addr;
/* Stack frame for unwinding */
frame->fp = tf->tf_x[29];
frame->lr = tf->tf_lr;
/* Translate the signal. */
sig = bsd_to_linux_signal(sig);
siginfo_to_lsiginfo(&ksi->ksi_info, &frame->sf.sf_si, sig);
bsd_to_linux_sigset(mask, &frame->sf.sf_uc.uc_sigmask);
/*
* Prepare fpsimd & esr. Does not check sizes, as
* __reserved is big enougth.
*/
scr = (uint8_t *)&frame->sf.sf_uc.uc_sc.__reserved;
#ifdef VFP
fpsimd = (struct l_fpsimd_context *) scr;
fpsimd->head.magic = L_FPSIMD_MAGIC;
fpsimd->head.size = sizeof(struct l_fpsimd_context);
fpsimd->fpsr = uc.uc_mcontext.mc_fpregs.fp_sr;
fpsimd->fpcr = uc.uc_mcontext.mc_fpregs.fp_cr;
memcpy(fpsimd->vregs, &uc.uc_mcontext.mc_fpregs.fp_q,
sizeof(uc.uc_mcontext.mc_fpregs.fp_q));
scr += roundup(sizeof(struct l_fpsimd_context), 16);
#endif
if (ksi->ksi_addr != 0) {
esr = (struct l_esr_context *) scr;
esr->head.magic = L_ESR_MAGIC;
esr->head.size = sizeof(struct l_esr_context);
esr->esr = tf->tf_esr;
}
memcpy(&frame->sf.sf_uc.uc_stack, &uc_stack, sizeof(uc_stack));
memcpy(&frame->uc, &uc, sizeof(uc));
/* Copy the sigframe out to the user's stack. */
if (copyout(&frame, fp, sizeof(*fp)) != 0) {
if (copyout(frame, fp, sizeof(*fp)) != 0) {
/* Process has trashed its stack. Kill it. */
free(frame, M_LINUX);
CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp);
PROC_LOCK(p);
sigexit(td, SIGILL);
}
free(frame, M_LINUX);
tf->tf_x[0]= sig;
tf->tf_x[1] = (register_t)&fp->sf_si;
tf->tf_x[2] = (register_t)&fp->sf_uc;
tf->tf_elr = (register_t)catcher;
tf->tf_x[1] = (register_t)&fp->sf.sf_si;
tf->tf_x[2] = (register_t)&fp->sf.sf_uc;
tf->tf_x[8] = (register_t)catcher;
tf->tf_sp = (register_t)fp;
tf->tf_lr = (register_t)__kernel_rt_sigreturn;
tf->tf_elr = (register_t)linux_vdso_sigcode;
CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_elr,
tf->tf_sp);

View File

@ -1,6 +1,6 @@
/*
* Linker script for 64-bit vDSO.
* Copied from Linux kernel arch/x86/vdso/vdso-layout.lds.S
* Copied from Linux kernel arch/arm64/kernel/vdso/vdso.lds.S
*
* $FreeBSD$
*/
@ -17,29 +17,32 @@ SECTIONS
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
/DISCARD/ : {
*(.note.GNU-stack .note.gnu.property)
}
.note : { *(.note.*) } :text :note
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
.eh_frame : { KEEP (*(.eh_frame)) } :text
. = ALIGN(0x100);
.text : { *(.text*) } :text =0x90909090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.dynamic : { *(.dynamic) } :text :dynamic
.rodata : { *(.rodata*) } :text
.data : {
*(.data*)
*(.sdata*)
*(.got.plt) *(.got)
*(.gnu.linkonce.d.*)
*(.bss*)
*(.dynbss*)
*(.gnu.linkonce.b.*)
*(.data*)
}
.altinstructions : { *(.altinstructions) }
.altinstr_replacement : { *(.altinstr_replacement) }
_end = .;
PROVIDE(end = .);
. = ALIGN(0x100);
.text : { *(.test .text*) } :text =0x90909090
/DISCARD/ : {
*(.eh_frame .eh_frame_hdr)
}
}
PHDRS
@ -47,7 +50,6 @@ PHDRS
text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
note PT_NOTE FLAGS(4); /* PF_R */
eh_frame_hdr PT_GNU_EH_FRAME;
}
/*
@ -68,6 +70,7 @@ VERSION
global:
linux_platform;
kern_timekeep_base;
linux_vdso_sigcode;
local: *;
};
}