linux-user/i386: Add vdso

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1267
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-07-01 12:07:44 -07:00
parent 2fa536d107
commit a1367443ba
8 changed files with 271 additions and 2 deletions

View File

@ -305,12 +305,27 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en
(*regs)[15] = tswapreg(env->regs[R_ESP]);
(*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff);
}
#endif
/*
* i386 is the only target which supplies AT_SYSINFO for the vdso.
* All others only supply AT_SYSINFO_EHDR.
*/
#define DLINFO_ARCH_ITEMS (vdso_info != NULL)
#define ARCH_DLINFO \
do { \
if (vdso_info) { \
NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \
} \
} while (0)
#define VDSO_HEADER "vdso.c.inc"
#endif /* TARGET_X86_64 */
#define USE_ELF_CORE_DUMP
#define ELF_EXEC_PAGESIZE 4096
#endif
#endif /* TARGET_I386 */
#ifdef TARGET_ARM

View File

@ -0,0 +1,11 @@
include $(BUILD_DIR)/tests/tcg/i386-linux-user/config-target.mak
SUBDIR = $(SRC_PATH)/linux-user/i386
VPATH += $(SUBDIR)
all: $(SUBDIR)/vdso.so
$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h
$(CC) -o $@ -m32 -nostdlib -shared -Wl,-h,linux-gate.so.1 \
-Wl,--build-id=sha1 -Wl,--hash-style=both \
-Wl,-T,$(SUBDIR)/vdso.ld $<

View File

@ -3,3 +3,10 @@ syscall_nr_generators += {
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
output: '@BASENAME@_nr.h')
}
vdso_inc = gen_vdso.process('vdso.so', extra_args: [
'-s', '__kernel_sigreturn',
'-r', '__kernel_rt_sigreturn'
])
linux_user_ss.add(when: 'TARGET_I386', if_true: vdso_inc)

View File

@ -214,6 +214,17 @@ struct rt_sigframe {
};
#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
/*
* Verify that vdso-asmoffset.h constants match.
*/
#include "i386/vdso-asmoffset.h"
QEMU_BUILD_BUG_ON(offsetof(struct sigframe, sc.eip)
!= SIGFRAME_SIGCONTEXT_eip);
QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, uc.tuc_mcontext.eip)
!= RT_SIGFRAME_SIGCONTEXT_eip);
#else
struct rt_sigframe {

View File

@ -0,0 +1,6 @@
/*
* offsetof(struct sigframe, sc.eip)
* offsetof(struct rt_sigframe, uc.tuc_mcontext.eip)
*/
#define SIGFRAME_SIGCONTEXT_eip 64
#define RT_SIGFRAME_SIGCONTEXT_eip 220

143
linux-user/i386/vdso.S Normal file
View File

@ -0,0 +1,143 @@
/*
* i386 linux replacement vdso.
*
* Copyright 2023 Linaro, Ltd.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <asm/unistd.h>
#include "vdso-asmoffset.h"
.macro endf name
.globl \name
.type \name, @function
.size \name, . - \name
.endm
.macro vdso_syscall1 name, nr
\name:
.cfi_startproc
mov %ebx, %edx
.cfi_register %ebx, %edx
mov 4(%esp), %ebx
mov $\nr, %eax
int $0x80
mov %edx, %ebx
ret
.cfi_endproc
endf \name
.endm
.macro vdso_syscall2 name, nr
\name:
.cfi_startproc
mov %ebx, %edx
.cfi_register %ebx, %edx
mov 4(%esp), %ebx
mov 8(%esp), %ecx
mov $\nr, %eax
int $0x80
mov %edx, %ebx
ret
.cfi_endproc
endf \name
.endm
.macro vdso_syscall3 name, nr
\name:
.cfi_startproc
push %ebx
.cfi_adjust_cfa_offset 4
.cfi_rel_offset %ebx, 0
mov 8(%esp), %ebx
mov 12(%esp), %ecx
mov 16(%esp), %edx
mov $\nr, %eax
int $0x80
pop %ebx
.cfi_adjust_cfa_offset -4
.cfi_restore %ebx
ret
.cfi_endproc
endf \name
.endm
__kernel_vsyscall:
.cfi_startproc
int $0x80
ret
.cfi_endproc
endf __kernel_vsyscall
vdso_syscall2 __vdso_clock_gettime, __NR_clock_gettime
vdso_syscall2 __vdso_clock_gettime64, __NR_clock_gettime64
vdso_syscall2 __vdso_clock_getres, __NR_clock_getres
vdso_syscall2 __vdso_gettimeofday, __NR_gettimeofday
vdso_syscall1 __vdso_time, __NR_time
vdso_syscall3 __vdso_getcpu, __NR_gettimeofday
/*
* Signal return handlers.
*/
.cfi_startproc simple
.cfi_signal_frame
/*
* For convenience, put the cfa just above eip in sigcontext, and count
* offsets backward from there. Re-compute the cfa in the two contexts
* we have for signal unwinding. This is far simpler than the
* DW_CFA_expression form that the kernel uses, and is equally correct.
*/
.cfi_def_cfa %esp, SIGFRAME_SIGCONTEXT_eip + 4
.cfi_offset %eip, -4
/* err, -8 */
/* trapno, -12 */
.cfi_offset %eax, -16
.cfi_offset %ecx, -20
.cfi_offset %edx, -24
.cfi_offset %ebx, -28
.cfi_offset %esp, -32
.cfi_offset %ebp, -36
.cfi_offset %esi, -40
.cfi_offset %edi, -44
/*
* While this frame is marked as a signal frame, that only applies to how
* the return address is handled for the outer frame. The return address
* that arrived here, from the inner frame, is not marked as a signal frame
* and so the unwinder still tries to subtract 1 to examine the presumed
* call insn. Thus we must extend the unwind info to a nop before the start.
*/
nop
__kernel_sigreturn:
popl %eax /* pop sig */
.cfi_adjust_cfa_offset -4
movl $__NR_sigreturn, %eax
int $0x80
endf __kernel_sigreturn
.cfi_def_cfa_offset RT_SIGFRAME_SIGCONTEXT_eip + 4
nop
__kernel_rt_sigreturn:
movl $__NR_rt_sigreturn, %eax
int $0x80
endf __kernel_rt_sigreturn
.cfi_endproc
/*
* TODO: Add elf notes. E.g.
*
* #include <linux/elfnote.h>
* ELFNOTE_START(Linux, 0, "a")
* .long LINUX_VERSION_CODE
* ELFNOTE_END
*
* but what version number would we set for QEMU?
*/

76
linux-user/i386/vdso.ld Normal file
View File

@ -0,0 +1,76 @@
/*
* Linker script for linux i386 replacement vdso.
*
* Copyright 2023 Linaro, Ltd.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
ENTRY(__kernel_vsyscall)
VERSION {
LINUX_2.6 {
global:
__vdso_clock_gettime;
__vdso_gettimeofday;
__vdso_time;
__vdso_clock_getres;
__vdso_clock_gettime64;
__vdso_getcpu;
};
LINUX_2.5 {
global:
__kernel_vsyscall;
__kernel_sigreturn;
__kernel_rt_sigreturn;
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 =0x90909090
}

BIN
linux-user/i386/vdso.so Executable file

Binary file not shown.