linux-user/sh4: add VDSO support for sh4 and sh4eb
Provides replacement VDSO with sigreturn trampolines (__kernel_sigreturn, __kernel_rt_sigreturn) and syscall stubs (clock_gettime, clock_gettime64, clock_getres, gettimeofday). Both LE and BE blobs are committed and selected at compile time via TARGET_BIG_ENDIAN. The BE variant requires an sh4eb-unknown-linux-gnu toolchain; sh4-unknown-linux-gnu does not support -mb. CFI register numbers follow GCC's SH_DEBUGGER_REGNO: PR=17, GBR=18, MACH=20, MACL=21, FPUL=23, FPSCR=24, FR0-15=25-40. Signed-off-by: Matt Turner <mattst88@gmail.com> Signed-off-by: Helge Deller <deller@gmx.de>
Matt Turner committed
May 23, 2026 at 07:41 UTC
975bd51a88792cb6a3596c7127a1009bb3a17f08
9 files changed
+299
linux-user/sh4/Makefile.vdso
new
+18
@@ -0,0 +1,18 @@
1
+include $(BUILD_DIR)/tests/tcg/sh4-linux-user/config-target.mak
2
+
3
+SUBDIR = $(SRC_PATH)/linux-user/sh4
4
+VPATH += $(SUBDIR)
5
+
6
+all: $(SUBDIR)/vdso-le.so $(SUBDIR)/vdso-be.so
7
+
8
+LDFLAGS = -nostdlib -shared -Wl,-h,linux-gate.so.1 \
9
+ -Wl,--build-id=sha1 -Wl,--hash-style=both \
10
+ -Wl,-T,$(SUBDIR)/vdso.ld
11
+
12
+$(SUBDIR)/vdso-le.so: vdso.S vdso.ld vdso-asmoffset.h
13
+ $(CC) -o $@ $(LDFLAGS) -ml $<
14
+
15
+CC_BE = sh4eb-unknown-linux-gnu-gcc
16
+
17
+$(SUBDIR)/vdso-be.so: vdso.S vdso.ld vdso-asmoffset.h
18
+ $(CC_BE) -o $@ $(LDFLAGS) $<
linux-user/sh4/elfload.c
+14
@@ -5,6 +5,20 @@
5
#include "loader.h"
6
#include "target_elf.h"
7
8
+#if TARGET_BIG_ENDIAN
9
+# include "vdso-be.c.inc"
10
+#else
11
+# include "vdso-le.c.inc"
12
+#endif
13
+
14
+const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags G_GNUC_UNUSED)
15
+{
16
+#if TARGET_BIG_ENDIAN
17
+ return &vdso_be_image_info;
18
+#else
19
+ return &vdso_le_image_info;
20
+#endif
21
+}
22
23
const char *get_elf_cpu_model(uint32_t eflags)
24
{
linux-user/sh4/meson.build
+12
@@ -3,3 +3,15 @@ syscall_nr_generators += {
3
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
4
output: '@BASENAME@_nr.h')
5
}
6
+
7
+vdso_le_inc = gen_vdso.process('vdso-le.so',
8
+ extra_args: ['-s', '__kernel_sigreturn',
9
+ '-r', '__kernel_rt_sigreturn',
10
+ '-p', 'vdso_le'])
11
+
12
+vdso_be_inc = gen_vdso.process('vdso-be.so',
13
+ extra_args: ['-s', '__kernel_sigreturn',
14
+ '-r', '__kernel_rt_sigreturn',
15
+ '-p', 'vdso_be'])
16
+
17
+linux_user_ss.add(when: 'TARGET_SH4', if_true: [vdso_le_inc, vdso_be_inc])
linux-user/sh4/target_elf.h
+1
@@ -15,6 +15,7 @@
15
16
#define HAVE_ELF_HWCAP 1
17
#define HAVE_ELF_CORE_DUMP 1
18
+#define HAVE_VDSO_IMAGE_INFO 1
19
20
/*
21
* See linux kernel: arch/sh/include/asm/elf.h, where
linux-user/sh4/vdso-asmoffset.h
new
+45
@@ -0,0 +1,45 @@
1
+/*
2
+ * Offsets into target signal frames for sh4 vdso.
3
+ *
4
+ * From linux-user/sh4/signal.c:
5
+ *
6
+ * struct target_sigcontext {
7
+ * target_ulong oldmask; // 0
8
+ * target_ulong sc_gregs[16]; // 4
9
+ * target_ulong sc_pc; // 68
10
+ * target_ulong sc_pr; // 72
11
+ * target_ulong sc_sr; // 76
12
+ * target_ulong sc_gbr; // 80
13
+ * target_ulong sc_mach; // 84
14
+ * target_ulong sc_macl; // 88
15
+ * target_ulong sc_fpregs[16]; // 92
16
+ * target_ulong sc_xfpregs[16]; // 156
17
+ * unsigned int sc_fpscr; // 220
18
+ * unsigned int sc_fpul; // 224
19
+ * unsigned int sc_ownedfp; // 228
20
+ * }; // sizeof = 232
21
+ *
22
+ * struct sigframe { sigcontext sc; ... }
23
+ * struct rt_sigframe { siginfo info[128]; ucontext uc; }
24
+ * ucontext = { flags[4], link[4], stack[12], sigcontext mcontext; ... }
25
+ * => mcontext at rt_sigframe + 128 + 20 = rt_sigframe + 148
26
+ */
27
+
28
+/* Offset of sigcontext within sigframe (CFA base for sigreturn). */
29
+#define SIGFRAME_SC_OFFSET 0
30
+
31
+/* Offset of tuc_mcontext within rt_sigframe (CFA base for rt_sigreturn). */
32
+#define RT_SIGFRAME_SC_OFFSET 148
33
+
34
+/* Offsets within struct sigcontext. */
35
+#define SC_GREGS 4
36
+#define SC_PC 68
37
+#define SC_PR 72
38
+#define SC_SR 76
39
+#define SC_GBR 80
40
+#define SC_MACH 84
41
+#define SC_MACL 88
42
+#define SC_FPREGS 92
43
+#define SC_XFPREGS 156
44
+#define SC_FPSCR 220
45
+#define SC_FPUL 224
linux-user/sh4/vdso-be.so
Binary files /dev/null and b/linux-user/sh4/vdso-be.so differ
linux-user/sh4/vdso-le.so
Binary files /dev/null and b/linux-user/sh4/vdso-le.so differ
linux-user/sh4/vdso.S
new
+142
@@ -0,0 +1,142 @@
1
+/*
2
+ * sh4 linux replacement vdso.
3
+ *
4
+ * Copyright 2023 Linaro, Ltd.
5
+ *
6
+ * SPDX-License-Identifier: GPL-2.0-or-later
7
+ */
8
+
9
+#include <asm/unistd.h>
10
+#include "vdso-asmoffset.h"
11
+
12
+ .text
13
+
14
+.macro endf name
15
+ .globl \name
16
+ .type \name, @function
17
+ .size \name, . - \name
18
+.endm
19
+
20
+/*
21
+ * SH4 syscall convention:
22
+ * Syscall number in r3 (caller-saved, so no save/restore needed)
23
+ * Arguments in r4-r7
24
+ * Return value in r0
25
+ * Syscall instruction: trapa #0x10
26
+ */
27
+
28
+.macro vdso_syscall name, nr
29
+\name:
30
+ .cfi_startproc
31
+ mov.l 1f, r3
32
+ trapa #0x10
33
+ rts
34
+ nop
35
+ .align 2
36
+1: .long \nr
37
+ .cfi_endproc
38
+endf \name
39
+.endm
40
+
41
+vdso_syscall __vdso_clock_gettime, __NR_clock_gettime
42
+vdso_syscall __vdso_clock_gettime64, __NR_clock_gettime64
43
+vdso_syscall __vdso_clock_getres, __NR_clock_getres
44
+vdso_syscall __vdso_gettimeofday, __NR_gettimeofday
45
+
46
+/*
47
+ * Signal return trampolines.
48
+ *
49
+ * For sigreturn: r15 points to struct sigframe; sigcontext is at
50
+ * offset SIGFRAME_SC_OFFSET (0).
51
+ * For rt_sigreturn: r15 points to struct rt_sigframe; sigcontext is at
52
+ * offset RT_SIGFRAME_SC_OFFSET (148).
53
+ *
54
+ * A single CFI region covers both trampolines. The CFA is set to the
55
+ * start of the relevant sigcontext; all register offsets are then
56
+ * identical for both trampolines. Between the two trampolines we use
57
+ * .cfi_def_cfa_offset to update the CFA base for the different layout.
58
+ */
59
+
60
+/*
61
+ * Start the unwind info at least one instruction before the signal
62
+ * trampoline, because the unwinder will assume we are returning
63
+ * after a call site.
64
+ */
65
+ .cfi_startproc simple
66
+ .cfi_signal_frame
67
+ .cfi_return_column 16 /* return column is PC */
68
+
69
+ /* CFA = r15 + SIGFRAME_SC_OFFSET = r15 (sigcontext base, sigreturn). */
70
+ .cfi_def_cfa 15, SIGFRAME_SC_OFFSET
71
+
72
+ /* Integer registers r0-r15: sc_gregs[n] at sigcontext + SC_GREGS + n*4. */
73
+ .cfi_offset 0, SC_GREGS + 0 * 4
74
+ .cfi_offset 1, SC_GREGS + 1 * 4
75
+ .cfi_offset 2, SC_GREGS + 2 * 4
76
+ .cfi_offset 3, SC_GREGS + 3 * 4
77
+ .cfi_offset 4, SC_GREGS + 4 * 4
78
+ .cfi_offset 5, SC_GREGS + 5 * 4
79
+ .cfi_offset 6, SC_GREGS + 6 * 4
80
+ .cfi_offset 7, SC_GREGS + 7 * 4
81
+ .cfi_offset 8, SC_GREGS + 8 * 4
82
+ .cfi_offset 9, SC_GREGS + 9 * 4
83
+ .cfi_offset 10, SC_GREGS + 10 * 4
84
+ .cfi_offset 11, SC_GREGS + 11 * 4
85
+ .cfi_offset 12, SC_GREGS + 12 * 4
86
+ .cfi_offset 13, SC_GREGS + 13 * 4
87
+ .cfi_offset 14, SC_GREGS + 14 * 4
88
+ .cfi_offset 15, SC_GREGS + 15 * 4
89
+
90
+ /* PC (return column). */
91
+ .cfi_offset 16, SC_PC
92
+
93
+ /* Control registers. */
94
+ .cfi_offset 17, SC_PR
95
+ .cfi_offset 18, SC_GBR
96
+ .cfi_offset 20, SC_MACH
97
+ .cfi_offset 21, SC_MACL
98
+
99
+ /* FP registers fr0-fr15: sc_fpregs[n] at sigcontext + SC_FPREGS + n*4. */
100
+ .cfi_offset 25, SC_FPREGS + 0 * 4
101
+ .cfi_offset 26, SC_FPREGS + 1 * 4
102
+ .cfi_offset 27, SC_FPREGS + 2 * 4
103
+ .cfi_offset 28, SC_FPREGS + 3 * 4
104
+ .cfi_offset 29, SC_FPREGS + 4 * 4
105
+ .cfi_offset 30, SC_FPREGS + 5 * 4
106
+ .cfi_offset 31, SC_FPREGS + 6 * 4
107
+ .cfi_offset 32, SC_FPREGS + 7 * 4
108
+ .cfi_offset 33, SC_FPREGS + 8 * 4
109
+ .cfi_offset 34, SC_FPREGS + 9 * 4
110
+ .cfi_offset 35, SC_FPREGS + 10 * 4
111
+ .cfi_offset 36, SC_FPREGS + 11 * 4
112
+ .cfi_offset 37, SC_FPREGS + 12 * 4
113
+ .cfi_offset 38, SC_FPREGS + 13 * 4
114
+ .cfi_offset 39, SC_FPREGS + 14 * 4
115
+ .cfi_offset 40, SC_FPREGS + 15 * 4
116
+
117
+ /* FPUL, FPSCR. */
118
+ .cfi_offset 23, SC_FPUL
119
+ .cfi_offset 24, SC_FPSCR
120
+
121
+ nop
122
+
123
+sigreturn_region_start:
124
+__kernel_sigreturn:
125
+ mov.l 1f, r3
126
+ trapa #0x10
127
+ .align 2
128
+1: .long __NR_sigreturn
129
+endf __kernel_sigreturn
130
+
131
+ /* Update CFA base for the rt_sigreturn frame layout. */
132
+ .cfi_def_cfa_offset RT_SIGFRAME_SC_OFFSET
133
+
134
+__kernel_rt_sigreturn:
135
+ mov.l 2f, r3
136
+ trapa #0x10
137
+ .align 2
138
+2: .long __NR_rt_sigreturn
139
+endf __kernel_rt_sigreturn
140
+sigreturn_region_end:
141
+
142
+ .cfi_endproc
linux-user/sh4/vdso.ld
new
+67
@@ -0,0 +1,67 @@
1
+/*
2
+ * Linker script for linux sh4 replacement vdso.
3
+ *
4
+ * Copyright 2023 Linaro, Ltd.
5
+ *
6
+ * SPDX-License-Identifier: GPL-2.0-or-later
7
+ */
8
+
9
+VERSION {
10
+ LINUX_2.6 {
11
+ global:
12
+ __vdso_clock_gettime;
13
+ __vdso_clock_gettime64;
14
+ __vdso_clock_getres;
15
+ __vdso_gettimeofday;
16
+ __kernel_sigreturn;
17
+ __kernel_rt_sigreturn;
18
+ local: *;
19
+ };
20
+}
21
+
22
+PHDRS {
23
+ phdr PT_PHDR FLAGS(4) PHDRS;
24
+ load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */
25
+ dynamic PT_DYNAMIC FLAGS(4);
26
+ eh_frame_hdr PT_GNU_EH_FRAME;
27
+ note PT_NOTE FLAGS(4);
28
+}
29
+
30
+SECTIONS {
31
+ . = SIZEOF_HEADERS;
32
+
33
+ /*
34
+ * The following, including the FILEHDRS and PHDRS, are modified
35
+ * when we relocate the binary. We want them to be initially
36
+ * writable for the relocation; we'll force them read-only after.
37
+ */
38
+ .note : { *(.note*) } :load :note
39
+ .dynamic : { *(.dynamic) } :load :dynamic
40
+ .dynsym : { *(.dynsym) } :load
41
+ .data : {
42
+ /*
43
+ * There ought not be any real read-write data.
44
+ * But since we manipulated the segment layout,
45
+ * we have to put these sections somewhere.
46
+ */
47
+ *(.data*)
48
+ *(.sdata*)
49
+ *(.got.plt) *(.got)
50
+ *(.gnu.linkonce.d.*)
51
+ *(.bss*)
52
+ *(.dynbss*)
53
+ *(.gnu.linkonce.b.*)
54
+ }
55
+
56
+ .rodata : { *(.rodata*) }
57
+ .hash : { *(.hash) }
58
+ .gnu.hash : { *(.gnu.hash) }
59
+ .dynstr : { *(.dynstr) }
60
+ .gnu.version : { *(.gnu.version) }
61
+ .gnu.version_d : { *(.gnu.version_d) }
62
+ .gnu.version_r : { *(.gnu.version_r) }
63
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr
64
+ .eh_frame : { *(.eh_frame) } :load
65
+
66
+ .text : { *(.text*) } :load
67
+}