git: c56480a83235 - main - linux(4): Implement signal trampoline for arm64 in a FreeBSD-way
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 15 May 2022 18:12:30 UTC
The branch main has been updated by dchagin:
URL: https://cgit.FreeBSD.org/src/commit/?id=c56480a832354aff995f9d0bc5da4ccf27dfe78a
commit c56480a832354aff995f9d0bc5da4ccf27dfe78a
Author: Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2022-05-15 18:10:50 +0000
Commit: Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-05-15 18:10:50 +0000
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
---
sys/arm64/linux/linux.h | 2 +-
sys/arm64/linux/linux_locore.asm | 15 +++++--
sys/arm64/linux/linux_sigframe.h | 62 ++++++++++++++++++++++-----
sys/arm64/linux/linux_sysvec.c | 90 ++++++++++++++++++++++++++++++----------
sys/arm64/linux/linux_vdso.lds.s | 33 ++++++++-------
5 files changed, 150 insertions(+), 52 deletions(-)
diff --git a/sys/arm64/linux/linux.h b/sys/arm64/linux/linux.h
index 402f7aa39bb9..dafec928c7e4 100644
--- a/sys/arm64/linux/linux.h
+++ b/sys/arm64/linux/linux.h
@@ -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);
diff --git a/sys/arm64/linux/linux_locore.asm b/sys/arm64/linux/linux_locore.asm
index 0311c2e7e7e9..dfaafba155f2 100644
--- a/sys/arm64/linux/linux_locore.asm
+++ b/sys/arm64/linux/linux_locore.asm
@@ -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)
diff --git a/sys/arm64/linux/linux_sigframe.h b/sys/arm64/linux/linux_sigframe.h
index 060b89c920ac..d0d870e51375 100644
--- a/sys/arm64/linux/linux_sigframe.h
+++ b/sys/arm64/linux/linux_sigframe.h
@@ -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_ */
diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c
index 3d3e64f15c6c..461a00bf5b33 100644
--- a/sys/arm64/linux/linux_sysvec.c
+++ b/sys/arm64/linux/linux_sysvec.c
@@ -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);
+ get_mcontext(td, &uc.uc_mcontext, 0);
+ uc.uc_sigmask = *mask;
+
+ 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 */
- bzero(&frame, sizeof(frame));
- get_mcontext(td, &frame.sf_uc.uc_mcontext, 0);
+ 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);
- 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;
- mtx_unlock(&psp->ps_mtx);
- PROC_UNLOCK(td->td_proc);
+ /*
+ * 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);
diff --git a/sys/arm64/linux/linux_vdso.lds.s b/sys/arm64/linux/linux_vdso.lds.s
index 98cbb9a5736b..8790e14bbb80 100644
--- a/sys/arm64/linux/linux_vdso.lds.s
+++ b/sys/arm64/linux/linux_vdso.lds.s
@@ -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: *;
};
}