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: *; }; }