aboutsummaryrefslogtreecommitdiff
path: root/linux-user/arm
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2023-10-31 07:12:40 +0900
committerStefan Hajnoczi <stefanha@redhat.com>2023-10-31 07:12:40 +0900
commit516fffc9933cb21fad41ca8f7bf465d238d4d375 (patch)
tree7f5e6762561bcc7bddada935879ef973246a375d /linux-user/arm
parent235fe6d06e62d21439451ff7612458770a3df68f (diff)
parent335b8f700c42a011cf2855c47bf098be3d35bde4 (diff)
downloadqemu-516fffc9933cb21fad41ca8f7bf465d238d4d375.zip
qemu-516fffc9933cb21fad41ca8f7bf465d238d4d375.tar.gz
qemu-516fffc9933cb21fad41ca8f7bf465d238d4d375.tar.bz2
Merge tag 'pull-lu-20231030' of https://gitlab.com/rth7680/qemu into staging
linux-user: Fix guest signal remapping after adjusting SIGABRT linux-user: Implement VDSOs * tag 'pull-lu-20231030' of https://gitlab.com/rth7680/qemu: (21 commits) build: Add update-linux-vdso makefile rule linux-user: Show vdso address in /proc/pid/maps linux-user/s390x: Add vdso linux-user/s390x: Rename __SIGNAL_FRAMESIZE to STACK_FRAME_OVERHEAD linux-user/ppc: Add vdso linux-user/loongarch64: Add vdso linux-user/riscv: Add vdso linux-user/hppa: Add vdso linux-user/arm: Add vdso linux-user/aarch64: Add vdso linux-user/x86_64: Add vdso linux-user/i386: Add vdso linux-user: Add gen-vdso tool linux-user: Load vdso image if available linux-user: Replace bprm->fd with bprm->src.fd linux-user: Use ImageSource in load_symbols linux-user: Use ImageSource in load_elf_image linux-user: Do not clobber bprm_buf swapping ehdr linux-user: Tidy loader_exec linux-user: Introduce imgsrc_read, imgsrc_read_alloc ... Conflicts: linux-user/arm/signal.c Fix an #include context conflict. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'linux-user/arm')
-rw-r--r--linux-user/arm/Makefile.vdso17
-rw-r--r--linux-user/arm/meson.build12
-rw-r--r--linux-user/arm/signal.c49
-rw-r--r--linux-user/arm/vdso-asmoffset.h3
-rwxr-xr-xlinux-user/arm/vdso-be.sobin0 -> 2648 bytes
-rwxr-xr-xlinux-user/arm/vdso-le.sobin0 -> 2648 bytes
-rw-r--r--linux-user/arm/vdso.S174
-rw-r--r--linux-user/arm/vdso.ld67
8 files changed, 305 insertions, 17 deletions
diff --git a/linux-user/arm/Makefile.vdso b/linux-user/arm/Makefile.vdso
new file mode 100644
index 0000000..2d098a5
--- /dev/null
+++ b/linux-user/arm/Makefile.vdso
@@ -0,0 +1,17 @@
+include $(BUILD_DIR)/tests/tcg/arm-linux-user/config-target.mak
+
+SUBDIR = $(SRC_PATH)/linux-user/arm
+VPATH += $(SUBDIR)
+
+all: $(SUBDIR)/vdso-be.so $(SUBDIR)/vdso-le.so
+
+# Adding -use-blx disables unneeded interworking without actually using blx.
+LDFLAGS = -nostdlib -shared -Wl,-use-blx \
+ -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \
+ -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld
+
+$(SUBDIR)/vdso-be.so: vdso.S vdso.ld vdso-asmoffset.h
+ $(CC) -o $@ $(LDFLAGS) -mbig-endian $<
+
+$(SUBDIR)/vdso-le.so: vdso.S vdso.ld vdso-asmoffset.h
+ $(CC) -o $@ $(LDFLAGS) -mlittle-endian $<
diff --git a/linux-user/arm/meson.build b/linux-user/arm/meson.build
index 5a93c92..c4bb9af 100644
--- a/linux-user/arm/meson.build
+++ b/linux-user/arm/meson.build
@@ -5,3 +5,15 @@ syscall_nr_generators += {
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
output: '@BASENAME@_nr.h')
}
+
+# TARGET_BIG_ENDIAN is defined to 'n' for little-endian; which means it
+# is always true as far as source_set.apply() is concerned. Always build
+# both header files and include the right one via #if.
+
+vdso_be_inc = gen_vdso.process('vdso-be.so',
+ extra_args: ['-s', 'sigreturn_codes'])
+
+vdso_le_inc = gen_vdso.process('vdso-le.so',
+ extra_args: ['-s', 'sigreturn_codes'])
+
+linux_user_ss.add(when: 'TARGET_ARM', if_true: [vdso_be_inc, vdso_le_inc])
diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c
index 4020601..f77f692 100644
--- a/linux-user/arm/signal.c
+++ b/linux-user/arm/signal.c
@@ -22,6 +22,7 @@
#include "signal-common.h"
#include "linux-user/trace.h"
#include "target/arm/cpu-features.h"
+#include "vdso-asmoffset.h"
struct target_sigcontext {
abi_ulong trap_no;
@@ -103,6 +104,11 @@ struct rt_sigframe
struct sigframe sig;
};
+QEMU_BUILD_BUG_ON(offsetof(struct sigframe, retcode[3])
+ != SIGFRAME_RC3_OFFSET);
+QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, sig.retcode[3])
+ != RT_SIGFRAME_RC3_OFFSET);
+
static abi_ptr sigreturn_fdpic_tramp;
/*
@@ -161,6 +167,9 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize)
return (sp - framesize) & ~7;
}
+static void write_arm_sigreturn(uint32_t *rc, int syscall);
+static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs);
+
static int
setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
struct sigframe *frame, abi_ulong sp_addr)
@@ -168,9 +177,9 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
abi_ulong handler = 0;
abi_ulong handler_fdpic_GOT = 0;
abi_ulong retcode;
- int thumb, retcode_idx;
- int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info);
- bool copy_retcode;
+ bool is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info);
+ bool is_rt = ka->sa_flags & TARGET_SA_SIGINFO;
+ bool thumb;
if (is_fdpic) {
/* In FDPIC mode, ka->_sa_handler points to a function
@@ -185,9 +194,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
} else {
handler = ka->_sa_handler;
}
-
thumb = handler & 1;
- retcode_idx = thumb + (ka->sa_flags & TARGET_SA_SIGINFO ? 2 : 0);
uint32_t cpsr = cpsr_read(env);
@@ -203,24 +210,32 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
cpsr &= ~CPSR_E;
}
+ /* Our vdso default_sigreturn label is a table of entry points. */
+ retcode = default_sigreturn + (is_fdpic * 2 + is_rt) * 8;
+
+ /*
+ * Put the sigreturn code on the stack no matter which return
+ * mechanism we use in order to remain ABI compliant.
+ * Because this is about ABI, always use the A32 instructions,
+ * despite the fact that our actual vdso trampoline is T16.
+ */
+ if (is_fdpic) {
+ write_arm_fdpic_sigreturn(frame->retcode,
+ is_rt ? RT_SIGFRAME_RC3_OFFSET
+ : SIGFRAME_RC3_OFFSET);
+ } else {
+ write_arm_sigreturn(frame->retcode,
+ is_rt ? TARGET_NR_rt_sigreturn
+ : TARGET_NR_sigreturn);
+ }
+
if (ka->sa_flags & TARGET_SA_RESTORER) {
if (is_fdpic) {
+ /* Place the function descriptor in slot 3. */
__put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]);
- retcode = (sigreturn_fdpic_tramp +
- retcode_idx * RETCODE_BYTES + thumb);
- copy_retcode = true;
} else {
retcode = ka->sa_restorer;
- copy_retcode = false;
}
- } else {
- retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb;
- copy_retcode = true;
- }
-
- /* Copy the code to the stack slot for ABI compatibility. */
- if (copy_retcode) {
- memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES);
}
env->regs[0] = usig;
diff --git a/linux-user/arm/vdso-asmoffset.h b/linux-user/arm/vdso-asmoffset.h
new file mode 100644
index 0000000..252a95c
--- /dev/null
+++ b/linux-user/arm/vdso-asmoffset.h
@@ -0,0 +1,3 @@
+/* offsetof(struct sigframe, retcode[3]) */
+#define SIGFRAME_RC3_OFFSET 756
+#define RT_SIGFRAME_RC3_OFFSET 884
diff --git a/linux-user/arm/vdso-be.so b/linux-user/arm/vdso-be.so
new file mode 100755
index 0000000..69cafbb
--- /dev/null
+++ b/linux-user/arm/vdso-be.so
Binary files differ
diff --git a/linux-user/arm/vdso-le.so b/linux-user/arm/vdso-le.so
new file mode 100755
index 0000000..ad05a12
--- /dev/null
+++ b/linux-user/arm/vdso-le.so
Binary files differ
diff --git a/linux-user/arm/vdso.S b/linux-user/arm/vdso.S
new file mode 100644
index 0000000..b3bb649
--- /dev/null
+++ b/linux-user/arm/vdso.S
@@ -0,0 +1,174 @@
+/*
+ * arm linux replacement vdso.
+ *
+ * Copyright 2023 Linaro, Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <asm/unistd.h>
+#include "vdso-asmoffset.h"
+
+/*
+ * All supported cpus have T16 instructions: at least arm4t.
+ *
+ * We support user-user with m-profile cpus as an extension, because it
+ * is useful for testing gcc, which requires we avoid A32 instructions.
+ */
+ .thumb
+ .arch armv4t
+ .eabi_attribute Tag_FP_arch, 0
+ .eabi_attribute Tag_ARM_ISA_use, 0
+
+ .text
+
+.macro raw_syscall n
+ .ifne \n < 0x100
+ mov r7, #\n
+ .elseif \n < 0x1ff
+ mov r7, #0xff
+ add r7, #(\n - 0xff)
+ .else
+ .err
+ .endif
+ swi #0
+.endm
+
+.macro fdpic_thunk ofs
+ ldr r3, [sp, #\ofs]
+ ldmia r2, {r2, r3}
+ mov r9, r3
+ bx r2
+.endm
+
+.macro endf name
+ .globl \name
+ .type \name, %function
+ .size \name, . - \name
+.endm
+
+/*
+ * We must save/restore r7 for the EABI syscall number.
+ * While we're doing that, we might as well save LR to get a free return,
+ * and a branch that is interworking back to ARMv5.
+ */
+
+.macro SYSCALL name, nr
+\name:
+ .cfi_startproc
+ push {r7, lr}
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r7, -8
+ .cfi_offset lr, -4
+ raw_syscall \nr
+ pop {r7, pc}
+ .cfi_endproc
+endf \name
+.endm
+
+SYSCALL __vdso_clock_gettime, __NR_clock_gettime
+SYSCALL __vdso_clock_gettime64, __NR_clock_gettime64
+SYSCALL __vdso_clock_getres, __NR_clock_getres
+SYSCALL __vdso_gettimeofday, __NR_gettimeofday
+
+
+/*
+ * We, like the real kernel, use a table of sigreturn trampolines.
+ * Unlike the real kernel, we do not attempt to pack this into as
+ * few bytes as possible -- simply use 8 bytes per slot.
+ *
+ * Within each slot, use the exact same code sequence as the kernel,
+ * lest we trip up someone doing code inspection.
+ */
+
+.macro slot n
+ .balign 8
+ .org sigreturn_codes + 8 * \n
+.endm
+
+.macro cfi_fdpic_r9 ofs
+ /*
+ * fd = *(r13 + ofs)
+ * r9 = *(fd + 4)
+ *
+ * DW_CFA_expression r9, length (7),
+ * DW_OP_breg13, ofs, DW_OP_deref,
+ * DW_OP_plus_uconst, 4, DW_OP_deref
+ */
+ .cfi_escape 0x10, 9, 7, 0x7d, (\ofs & 0x7f) + 0x80, (\ofs >> 7), 0x06, 0x23, 4, 0x06
+.endm
+
+.macro cfi_fdpic_pc ofs
+ /*
+ * fd = *(r13 + ofs)
+ * pc = *fd
+ *
+ * DW_CFA_expression lr (14), length (5),
+ * DW_OP_breg13, ofs, DW_OP_deref, DW_OP_deref
+ */
+ .cfi_escape 0x10, 14, 5, 0x7d, (\ofs & 0x7f) + 0x80, (\ofs >> 7), 0x06, 0x06
+.endm
+
+/*
+ * Start the unwind info at least one instruction before the signal
+ * trampoline, because the unwinder will assume we are returning
+ * after a call site.
+ */
+ .cfi_startproc simple
+ .cfi_signal_frame
+ .cfi_return_column 15
+
+ .cfi_def_cfa sp, 32 + 64
+ .cfi_offset r0, -16 * 4
+ .cfi_offset r1, -15 * 4
+ .cfi_offset r2, -14 * 4
+ .cfi_offset r3, -13 * 4
+ .cfi_offset r4, -12 * 4
+ .cfi_offset r5, -11 * 4
+ .cfi_offset r6, -10 * 4
+ .cfi_offset r7, -9 * 4
+ .cfi_offset r8, -8 * 4
+ .cfi_offset r9, -7 * 4
+ .cfi_offset r10, -6 * 4
+ .cfi_offset r11, -5 * 4
+ .cfi_offset r12, -4 * 4
+ .cfi_offset r13, -3 * 4
+ .cfi_offset r14, -2 * 4
+ .cfi_offset r15, -1 * 4
+
+ nop
+
+ .balign 16
+sigreturn_codes:
+ /* [EO]ABI sigreturn */
+ slot 0
+ raw_syscall __NR_sigreturn
+
+ .cfi_def_cfa_offset 160 + 64
+
+ /* [EO]ABI rt_sigreturn */
+ slot 1
+ raw_syscall __NR_rt_sigreturn
+
+ .cfi_endproc
+
+ /* FDPIC sigreturn */
+ .cfi_startproc
+ cfi_fdpic_pc SIGFRAME_RC3_OFFSET
+ cfi_fdpic_r9 SIGFRAME_RC3_OFFSET
+
+ slot 2
+ fdpic_thunk SIGFRAME_RC3_OFFSET
+ .cfi_endproc
+
+ /* FDPIC rt_sigreturn */
+ .cfi_startproc
+ cfi_fdpic_pc RT_SIGFRAME_RC3_OFFSET
+ cfi_fdpic_r9 RT_SIGFRAME_RC3_OFFSET
+
+ slot 3
+ fdpic_thunk RT_SIGFRAME_RC3_OFFSET
+ .cfi_endproc
+
+ .balign 16
+endf sigreturn_codes
diff --git a/linux-user/arm/vdso.ld b/linux-user/arm/vdso.ld
new file mode 100644
index 0000000..3b00adf
--- /dev/null
+++ b/linux-user/arm/vdso.ld
@@ -0,0 +1,67 @@
+/*
+ * Linker script for linux arm replacement vdso.
+ *
+ * Copyright 2023 Linaro, Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+VERSION {
+ LINUX_2.6 {
+ global:
+ __vdso_clock_gettime;
+ __vdso_gettimeofday;
+ __vdso_clock_getres;
+ __vdso_clock_gettime64;
+
+ local: *;
+ };
+}
+
+
+PHDRS {
+ phdr PT_PHDR FLAGS(4) PHDRS;
+ load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */
+ dynamic PT_DYNAMIC FLAGS(4);
+ eh_frame_hdr PT_GNU_EH_FRAME;
+ note PT_NOTE FLAGS(4);
+}
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+
+ /*
+ * The following, including the FILEHDRS and PHDRS, are modified
+ * when we relocate the binary. We want them to be initially
+ * writable for the relocation; we'll force them read-only after.
+ */
+ .note : { *(.note*) } :load :note
+ .dynamic : { *(.dynamic) } :load :dynamic
+ .dynsym : { *(.dynsym) } :load
+ /*
+ * There ought not be any real read-write data.
+ * But since we manipulated the segment layout,
+ * we have to put these sections somewhere.
+ */
+ .data : {
+ *(.data*)
+ *(.sdata*)
+ *(.got.plt) *(.got)
+ *(.gnu.linkonce.d.*)
+ *(.bss*)
+ *(.dynbss*)
+ *(.gnu.linkonce.b.*)
+ }
+
+ .rodata : { *(.rodata*) }
+ .hash : { *(.hash) }
+ .gnu.hash : { *(.gnu.hash) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr
+ .eh_frame : { *(.eh_frame) } :load
+
+ .text : { *(.text*) } :load
+}