@samitouri / QOSamiQemu / commits / 975bd51a88

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 +}