aboutsummaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2023-08-14 17:58:55 -0700
committerRichard Henderson <richard.henderson@linaro.org>2023-10-30 13:41:56 -0700
commite34136d93059ddd4a5e186b62282fccf27c3e9d0 (patch)
tree918b4228f511d5b92dd352c159dfaa8eb8e36683 /linux-user
parent00cc2934b2f02c469bd28cae0f1ac09e289a5ae9 (diff)
downloadqemu-e34136d93059ddd4a5e186b62282fccf27c3e9d0.zip
qemu-e34136d93059ddd4a5e186b62282fccf27c3e9d0.tar.gz
qemu-e34136d93059ddd4a5e186b62282fccf27c3e9d0.tar.bz2
linux-user/ppc: Add vdso
Add support in gen-vdso-elfn.c.inc for the DT_PPC64_OPT dynamic tag: this is an integer, so does not need relocation. Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/elfload.c8
-rw-r--r--linux-user/gen-vdso-elfn.c.inc7
-rw-r--r--linux-user/ppc/Makefile.vdso20
-rw-r--r--linux-user/ppc/meson.build12
-rw-r--r--linux-user/ppc/signal.c31
-rw-r--r--linux-user/ppc/vdso-32.ld70
-rwxr-xr-xlinux-user/ppc/vdso-32.sobin0 -> 3020 bytes
-rw-r--r--linux-user/ppc/vdso-64.ld68
-rwxr-xr-xlinux-user/ppc/vdso-64.sobin0 -> 3896 bytes
-rwxr-xr-xlinux-user/ppc/vdso-64le.sobin0 -> 3896 bytes
-rw-r--r--linux-user/ppc/vdso-asmoffset.h20
-rw-r--r--linux-user/ppc/vdso.S239
12 files changed, 467 insertions, 8 deletions
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 4e6e005..2660251 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1187,6 +1187,14 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en
#define USE_ELF_CORE_DUMP
#define ELF_EXEC_PAGESIZE 4096
+#ifndef TARGET_PPC64
+# define VDSO_HEADER "vdso-32.c.inc"
+#elif TARGET_BIG_ENDIAN
+# define VDSO_HEADER "vdso-64.c.inc"
+#else
+# define VDSO_HEADER "vdso-64le.c.inc"
+#endif
+
#endif
#ifdef TARGET_LOONGARCH64
diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc
index 7034c36..95856eb 100644
--- a/linux-user/gen-vdso-elfn.c.inc
+++ b/linux-user/gen-vdso-elfn.c.inc
@@ -273,7 +273,14 @@ static void elfN(process)(FILE *outf, void *buf, bool need_bswap)
errors++;
break;
+ case PT_LOPROC + 3:
+ if (ehdr->e_machine == EM_PPC64) {
+ break; /* DT_PPC64_OPT: integer bitmask */
+ }
+ goto do_default;
+
default:
+ do_default:
/* This is probably something target specific. */
fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n",
(unsigned long)tag);
diff --git a/linux-user/ppc/Makefile.vdso b/linux-user/ppc/Makefile.vdso
new file mode 100644
index 0000000..3ca3c6b
--- /dev/null
+++ b/linux-user/ppc/Makefile.vdso
@@ -0,0 +1,20 @@
+include $(BUILD_DIR)/tests/tcg/ppc64-linux-user/config-target.mak
+
+SUBDIR = $(SRC_PATH)/linux-user/ppc
+VPATH += $(SUBDIR)
+
+all: $(SUBDIR)/vdso-32.so $(SUBDIR)/vdso-64.so $(SUBDIR)/vdso-64le.so
+
+LDFLAGS32 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-32.ld \
+ -Wl,-h,linux-vdso32.so.1 -Wl,--hash-style=both -Wl,--build-id=sha1
+LDFLAGS64 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-64.ld \
+ -Wl,-h,linux-vdso64.so.1 -Wl,--hash-style=both -Wl,--build-id=sha1
+
+$(SUBDIR)/vdso-32.so: vdso.S vdso-32.ld vdso-asmoffset.h
+ $(CC) -o $@ $(LDFLAGS32) -m32 $<
+
+$(SUBDIR)/vdso-64.so: vdso.S vdso-64.ld vdso-asmoffset.h
+ $(CC) -o $@ $(LDFLAGS64) -mbig-endian $<
+
+$(SUBDIR)/vdso-64le.so: vdso.S vdso-64.ld vdso-asmoffset.h
+ $(CC) -o $@ $(LDFLAGS64) -mlittle-endian $<
diff --git a/linux-user/ppc/meson.build b/linux-user/ppc/meson.build
index 19fead7..80cacae 100644
--- a/linux-user/ppc/meson.build
+++ b/linux-user/ppc/meson.build
@@ -3,3 +3,15 @@ syscall_nr_generators += {
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
output: '@BASENAME@_nr.h')
}
+
+vdso_32_inc = gen_vdso.process('vdso-32.so', extra_args: [
+ '-s', '__kernel_sigtramp32',
+ '-r', '__kernel_sigtramp_rt32'
+ ])
+linux_user_ss.add(when: 'TARGET_PPC', if_true: vdso_32_inc)
+
+vdso_64_inc = gen_vdso.process('vdso-64.so',
+ extra_args: ['-r', '__kernel_sigtramp_rt64'])
+vdso_64le_inc = gen_vdso.process('vdso-64le.so',
+ extra_args: ['-r', '__kernel_sigtramp_rt64'])
+linux_user_ss.add(when: 'TARGET_PPC64', if_true: [vdso_64_inc, vdso_64le_inc])
diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c
index a616f20..7e73028 100644
--- a/linux-user/ppc/signal.c
+++ b/linux-user/ppc/signal.c
@@ -21,14 +21,7 @@
#include "user-internals.h"
#include "signal-common.h"
#include "linux-user/trace.h"
-
-/* Size of dummy stack frame allocated when calling signal handler.
- See arch/powerpc/include/asm/ptrace.h. */
-#if defined(TARGET_PPC64)
-#define SIGNAL_FRAMESIZE 128
-#else
-#define SIGNAL_FRAMESIZE 64
-#endif
+#include "vdso-asmoffset.h"
/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC;
on 64-bit PPC, sigcontext and mcontext are one and the same. */
@@ -73,6 +66,16 @@ struct target_mcontext {
#endif
};
+QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, mc_fregs)
+ != offsetof_mcontext_fregs);
+#if defined(TARGET_PPC64)
+QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, v_regs)
+ != offsetof_mcontext_vregs_ptr);
+#else
+QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, mc_vregs)
+ != offsetof_mcontext_vregs);
+#endif
+
/* See arch/powerpc/include/asm/sigcontext.h. */
struct target_sigcontext {
target_ulong _unused[4];
@@ -161,6 +164,7 @@ struct target_ucontext {
#endif
};
+#if !defined(TARGET_PPC64)
/* See arch/powerpc/kernel/signal_32.c. */
struct target_sigframe {
struct target_sigcontext sctx;
@@ -168,6 +172,10 @@ struct target_sigframe {
int32_t abigap[56];
};
+QEMU_BUILD_BUG_ON(offsetof(struct target_sigframe, mctx)
+ != offsetof_sigframe_mcontext);
+#endif
+
#if defined(TARGET_PPC64)
#define TARGET_TRAMP_SIZE 6
@@ -184,6 +192,10 @@ struct target_rt_sigframe {
char abigap[288];
} __attribute__((aligned(16)));
+QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe,
+ uc.tuc_sigcontext.mcontext)
+ != offsetof_rt_sigframe_mcontext);
+
#else
struct target_rt_sigframe {
@@ -192,6 +204,9 @@ struct target_rt_sigframe {
int32_t abigap[56];
};
+QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext)
+ != offsetof_rt_sigframe_mcontext);
+
#endif
#if defined(TARGET_PPC64)
diff --git a/linux-user/ppc/vdso-32.ld b/linux-user/ppc/vdso-32.ld
new file mode 100644
index 0000000..6962696
--- /dev/null
+++ b/linux-user/ppc/vdso-32.ld
@@ -0,0 +1,70 @@
+/*
+ * Linker script for linux powerpc64 replacement vdso.
+ *
+ * Copyright 2023 Linaro, Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+VERSION {
+ LINUX_2.6.15 {
+ global:
+ __kernel_gettimeofday;
+ __kernel_clock_gettime;
+ __kernel_clock_gettime64;
+ __kernel_clock_getres;
+ __kernel_time;
+ __kernel_sync_dicache;
+ __kernel_sigtramp32;
+ __kernel_sigtramp_rt32;
+ __kernel_getcpu;
+ 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
+ .data : {
+ /*
+ * There ought not be any real read-write data.
+ * But since we manipulated the segment layout,
+ * we have to put these sections somewhere.
+ */
+ *(.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
+}
diff --git a/linux-user/ppc/vdso-32.so b/linux-user/ppc/vdso-32.so
new file mode 100755
index 0000000..b19baaf
--- /dev/null
+++ b/linux-user/ppc/vdso-32.so
Binary files differ
diff --git a/linux-user/ppc/vdso-64.ld b/linux-user/ppc/vdso-64.ld
new file mode 100644
index 0000000..a55c65e
--- /dev/null
+++ b/linux-user/ppc/vdso-64.ld
@@ -0,0 +1,68 @@
+/*
+ * Linker script for linux powerpc64 replacement vdso.
+ *
+ * Copyright 2023 Linaro, Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+VERSION {
+ LINUX_2.6.15 {
+ global:
+ __kernel_gettimeofday;
+ __kernel_clock_gettime;
+ __kernel_clock_getres;
+ __kernel_sync_dicache;
+ __kernel_sigtramp_rt64;
+ __kernel_getcpu;
+ __kernel_time;
+ 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
+ .data : {
+ /*
+ * There ought not be any real read-write data.
+ * But since we manipulated the segment layout,
+ * we have to put these sections somewhere.
+ */
+ *(.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
+}
diff --git a/linux-user/ppc/vdso-64.so b/linux-user/ppc/vdso-64.so
new file mode 100755
index 0000000..913c831
--- /dev/null
+++ b/linux-user/ppc/vdso-64.so
Binary files differ
diff --git a/linux-user/ppc/vdso-64le.so b/linux-user/ppc/vdso-64le.so
new file mode 100755
index 0000000..258a03b
--- /dev/null
+++ b/linux-user/ppc/vdso-64le.so
Binary files differ
diff --git a/linux-user/ppc/vdso-asmoffset.h b/linux-user/ppc/vdso-asmoffset.h
new file mode 100644
index 0000000..6844c8c
--- /dev/null
+++ b/linux-user/ppc/vdso-asmoffset.h
@@ -0,0 +1,20 @@
+/*
+ * Size of dummy stack frame allocated when calling signal handler.
+ * See arch/powerpc/include/asm/ptrace.h.
+ */
+#ifdef TARGET_ABI32
+# define SIGNAL_FRAMESIZE 64
+#else
+# define SIGNAL_FRAMESIZE 128
+#endif
+
+#ifdef TARGET_ABI32
+# define offsetof_sigframe_mcontext 0x20
+# define offsetof_rt_sigframe_mcontext 0x140
+# define offsetof_mcontext_fregs 0xc0
+# define offsetof_mcontext_vregs 0x1d0
+#else
+# define offsetof_rt_sigframe_mcontext 0xe8
+# define offsetof_mcontext_fregs 0x180
+# define offsetof_mcontext_vregs_ptr 0x288
+#endif
diff --git a/linux-user/ppc/vdso.S b/linux-user/ppc/vdso.S
new file mode 100644
index 0000000..689010d
--- /dev/null
+++ b/linux-user/ppc/vdso.S
@@ -0,0 +1,239 @@
+/*
+ * PowerPC linux replacement vdso.
+ *
+ * Copyright 2023 Linaro, Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <asm/unistd.h>
+#include <asm/errno.h>
+
+#ifndef _ARCH_PPC64
+# define TARGET_ABI32
+#endif
+#include "vdso-asmoffset.h"
+
+
+ .text
+
+.macro endf name
+ .globl \name
+ .size \name, .-\name
+ /* For PPC64, functions have special linkage; we export pointers. */
+#ifndef _ARCH_PPC64
+ .type \name, @function
+#endif
+.endm
+
+.macro raw_syscall nr
+ addi 0, 0, \nr
+ sc
+.endm
+
+.macro vdso_syscall name, nr
+\name:
+ raw_syscall \nr
+ blr
+endf \name
+.endm
+
+ .cfi_startproc
+
+vdso_syscall __kernel_gettimeofday, __NR_gettimeofday
+vdso_syscall __kernel_clock_gettime, __NR_clock_gettime
+vdso_syscall __kernel_clock_getres, __NR_clock_getres
+vdso_syscall __kernel_getcpu, __NR_getcpu
+vdso_syscall __kernel_time, __NR_time
+
+#ifdef __NR_clock_gettime64
+vdso_syscall __kernel_clock_gettime64, __NR_clock_gettime64
+#endif
+
+__kernel_sync_dicache:
+ /* qemu does not need to flush caches */
+ blr
+endf __kernel_sync_dicache
+
+ .cfi_endproc
+
+/*
+ * TODO: __kernel_get_tbfreq
+ * This is probably a constant for QEMU.
+ */
+
+/*
+ * 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
+
+#ifdef _ARCH_PPC64
+# define __kernel_sigtramp_rt __kernel_sigtramp_rt64
+# define sizeof_reg 8
+#else
+# define __kernel_sigtramp_rt __kernel_sigtramp_rt32
+# define sizeof_reg 4
+#endif
+#define sizeof_freg 8
+#define sizeof_vreg 16
+
+ .cfi_def_cfa 1, SIGNAL_FRAMESIZE + offsetof_rt_sigframe_mcontext
+
+ /* Return address */
+ .cfi_return_column 67
+ .cfi_offset 67, 32 * sizeof_reg /* nip */
+
+ /* Integer registers */
+ .cfi_offset 0, 0 * sizeof_reg
+ .cfi_offset 1, 1 * sizeof_reg
+ .cfi_offset 2, 2 * sizeof_reg
+ .cfi_offset 3, 3 * sizeof_reg
+ .cfi_offset 4, 4 * sizeof_reg
+ .cfi_offset 5, 5 * sizeof_reg
+ .cfi_offset 6, 6 * sizeof_reg
+ .cfi_offset 7, 7 * sizeof_reg
+ .cfi_offset 8, 8 * sizeof_reg
+ .cfi_offset 9, 9 * sizeof_reg
+ .cfi_offset 10, 10 * sizeof_reg
+ .cfi_offset 11, 11 * sizeof_reg
+ .cfi_offset 12, 12 * sizeof_reg
+ .cfi_offset 13, 13 * sizeof_reg
+ .cfi_offset 14, 14 * sizeof_reg
+ .cfi_offset 15, 15 * sizeof_reg
+ .cfi_offset 16, 16 * sizeof_reg
+ .cfi_offset 17, 17 * sizeof_reg
+ .cfi_offset 18, 18 * sizeof_reg
+ .cfi_offset 19, 19 * sizeof_reg
+ .cfi_offset 20, 20 * sizeof_reg
+ .cfi_offset 21, 21 * sizeof_reg
+ .cfi_offset 22, 22 * sizeof_reg
+ .cfi_offset 23, 23 * sizeof_reg
+ .cfi_offset 24, 24 * sizeof_reg
+ .cfi_offset 25, 25 * sizeof_reg
+ .cfi_offset 26, 26 * sizeof_reg
+ .cfi_offset 27, 27 * sizeof_reg
+ .cfi_offset 28, 28 * sizeof_reg
+ .cfi_offset 29, 29 * sizeof_reg
+ .cfi_offset 30, 30 * sizeof_reg
+ .cfi_offset 31, 31 * sizeof_reg
+ .cfi_offset 65, 36 * sizeof_reg /* lr */
+ .cfi_offset 70, 38 * sizeof_reg /* ccr */
+
+ /* Floating point registers */
+ .cfi_offset 32, offsetof_mcontext_fregs
+ .cfi_offset 33, offsetof_mcontext_fregs + 1 * sizeof_freg
+ .cfi_offset 34, offsetof_mcontext_fregs + 2 * sizeof_freg
+ .cfi_offset 35, offsetof_mcontext_fregs + 3 * sizeof_freg
+ .cfi_offset 36, offsetof_mcontext_fregs + 4 * sizeof_freg
+ .cfi_offset 37, offsetof_mcontext_fregs + 5 * sizeof_freg
+ .cfi_offset 38, offsetof_mcontext_fregs + 6 * sizeof_freg
+ .cfi_offset 39, offsetof_mcontext_fregs + 7 * sizeof_freg
+ .cfi_offset 40, offsetof_mcontext_fregs + 8 * sizeof_freg
+ .cfi_offset 41, offsetof_mcontext_fregs + 9 * sizeof_freg
+ .cfi_offset 42, offsetof_mcontext_fregs + 10 * sizeof_freg
+ .cfi_offset 43, offsetof_mcontext_fregs + 11 * sizeof_freg
+ .cfi_offset 44, offsetof_mcontext_fregs + 12 * sizeof_freg
+ .cfi_offset 45, offsetof_mcontext_fregs + 13 * sizeof_freg
+ .cfi_offset 46, offsetof_mcontext_fregs + 14 * sizeof_freg
+ .cfi_offset 47, offsetof_mcontext_fregs + 15 * sizeof_freg
+ .cfi_offset 48, offsetof_mcontext_fregs + 16 * sizeof_freg
+ .cfi_offset 49, offsetof_mcontext_fregs + 17 * sizeof_freg
+ .cfi_offset 50, offsetof_mcontext_fregs + 18 * sizeof_freg
+ .cfi_offset 51, offsetof_mcontext_fregs + 19 * sizeof_freg
+ .cfi_offset 52, offsetof_mcontext_fregs + 20 * sizeof_freg
+ .cfi_offset 53, offsetof_mcontext_fregs + 21 * sizeof_freg
+ .cfi_offset 54, offsetof_mcontext_fregs + 22 * sizeof_freg
+ .cfi_offset 55, offsetof_mcontext_fregs + 23 * sizeof_freg
+ .cfi_offset 56, offsetof_mcontext_fregs + 24 * sizeof_freg
+ .cfi_offset 57, offsetof_mcontext_fregs + 25 * sizeof_freg
+ .cfi_offset 58, offsetof_mcontext_fregs + 26 * sizeof_freg
+ .cfi_offset 59, offsetof_mcontext_fregs + 27 * sizeof_freg
+ .cfi_offset 60, offsetof_mcontext_fregs + 28 * sizeof_freg
+ .cfi_offset 61, offsetof_mcontext_fregs + 29 * sizeof_freg
+ .cfi_offset 62, offsetof_mcontext_fregs + 30 * sizeof_freg
+ .cfi_offset 63, offsetof_mcontext_fregs + 31 * sizeof_freg
+
+ /*
+ * Unlike the kernel, unconditionally represent the Altivec/VSX regs.
+ * The space within the stack frame is always available, and most of
+ * our supported processors have them enabled. The only complication
+ * for PPC64 is the misalignment, so that we have to use indirection.
+ */
+.macro save_vreg_ofs reg, ofs
+#ifdef _ARCH_PPC64
+ /*
+ * vreg = *(cfa + offsetof(v_regs)) + ofs
+ *
+ * The CFA is input to the expression on the stack, so:
+ * DW_CFA_expression reg, length (7),
+ * DW_OP_plus_uconst (0x23), vreg_ptr, DW_OP_deref (0x06),
+ * DW_OP_plus_uconst (0x23), ofs
+ */
+ .cfi_escape 0x10, 77 + \reg, 7, 0x23, (offsetof_mcontext_vregs_ptr & 0x7f) + 0x80, offsetof_mcontext_vregs_ptr >> 7, 0x06, 0x23, (\ofs & 0x7f) | 0x80, \ofs >> 7
+#else
+ .cfi_offset 77 + \reg, offsetof_mcontext_vregs + \ofs
+#endif
+.endm
+
+.macro save_vreg reg
+ save_vreg_ofs \reg, (\reg * sizeof_vreg)
+.endm
+
+ save_vreg 0
+ save_vreg 1
+ save_vreg 2
+ save_vreg 3
+ save_vreg 4
+ save_vreg 5
+ save_vreg 6
+ save_vreg 7
+ save_vreg 8
+ save_vreg 9
+ save_vreg 10
+ save_vreg 11
+ save_vreg 12
+ save_vreg 13
+ save_vreg 14
+ save_vreg 15
+ save_vreg 16
+ save_vreg 17
+ save_vreg 18
+ save_vreg 19
+ save_vreg 20
+ save_vreg 21
+ save_vreg 22
+ save_vreg 23
+ save_vreg 24
+ save_vreg 25
+ save_vreg 26
+ save_vreg 27
+ save_vreg 28
+ save_vreg 29
+ save_vreg 30
+ save_vreg 31
+ save_vreg 32
+ save_vreg_ofs 33, (32 * sizeof_vreg + 12)
+
+ nop
+
+__kernel_sigtramp_rt:
+ raw_syscall __NR_rt_sigreturn
+endf __kernel_sigtramp_rt
+
+#ifndef _ARCH_PPC64
+ /*
+ * The non-rt sigreturn has the same layout at a different offset.
+ * Move the CFA and leave all othe other descriptions the same.
+ */
+ .cfi_def_cfa 1, SIGNAL_FRAMESIZE + offsetof_sigframe_mcontext
+ nop
+__kernel_sigtramp32:
+ raw_syscall __NR_sigreturn
+endf __kernel_sigtramp32
+#endif
+
+ .cfi_endproc