linux-user: Fix guest signal remapping after adjusting SIGABRT
linux-user: Implement VDSOs -----BEGIN PGP SIGNATURE----- iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmVAHMsdHHJpY2hhcmQu aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV/GSgf/SiaCzl7FV2NsxA2h zHrgSYEf/4dyqjbgNhE9XSrIJ/cPEY47JrpMqJ0cK4BGc/d2IppUU0Zz3qZltXck CkTIPPXEWDvex+PSe5NXarxQtOazi21C+EySGFtFcCQ32C/LsdJBtNzrB+G/Tl/t QvPJBztXvS6FAdVci2TGBNk62nFq3NS/Uz477SD6Q/uSlczQQ5b1fu3YgZcCqM9D ncncHbuExUu+NMK02h8vyWwpxaTvUBSdRxx/6jnyctwVpWyMaIOfsrMooz0gBfoD Z7MqXhvtBYOnm4OjcQs4Nj1JBOdYoQS/y6dJ7ZP0kg10VSEwr48pduXZSvIypxbw hsaa8w== =wcWF -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
516fffc993
10
Makefile
10
Makefile
@ -283,6 +283,13 @@ include $(SRC_PATH)/tests/vm/Makefile.include
|
|||||||
print-help-run = printf " %-30s - %s\\n" "$1" "$2"
|
print-help-run = printf " %-30s - %s\\n" "$1" "$2"
|
||||||
print-help = @$(call print-help-run,$1,$2)
|
print-help = @$(call print-help-run,$1,$2)
|
||||||
|
|
||||||
|
.PHONY: update-linux-vdso
|
||||||
|
update-linux-vdso:
|
||||||
|
@for m in $(SRC_PATH)/linux-user/*/Makefile.vdso; do \
|
||||||
|
$(MAKE) $(SUBDIR_MAKEFLAGS) -C $$(dirname $$m) -f Makefile.vdso \
|
||||||
|
SRC_PATH=$(SRC_PATH) BUILD_DIR=$(BUILD_DIR); \
|
||||||
|
done
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@echo 'Generic targets:'
|
@echo 'Generic targets:'
|
||||||
@ -303,6 +310,9 @@ endif
|
|||||||
$(call print-help,distclean,Remove all generated files)
|
$(call print-help,distclean,Remove all generated files)
|
||||||
$(call print-help,dist,Build a distributable tarball)
|
$(call print-help,dist,Build a distributable tarball)
|
||||||
@echo ''
|
@echo ''
|
||||||
|
@echo 'Linux-user targets:'
|
||||||
|
$(call print-help,update-linux-vdso,Build linux-user vdso images)
|
||||||
|
@echo ''
|
||||||
@echo 'Test targets:'
|
@echo 'Test targets:'
|
||||||
$(call print-help,check,Run all tests (check-help for details))
|
$(call print-help,check,Run all tests (check-help for details))
|
||||||
$(call print-help,bench,Run all benchmarks)
|
$(call print-help,bench,Run all benchmarks)
|
||||||
|
15
linux-user/aarch64/Makefile.vdso
Normal file
15
linux-user/aarch64/Makefile.vdso
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
include $(BUILD_DIR)/tests/tcg/aarch64-linux-user/config-target.mak
|
||||||
|
|
||||||
|
SUBDIR = $(SRC_PATH)/linux-user/aarch64
|
||||||
|
VPATH += $(SUBDIR)
|
||||||
|
|
||||||
|
all: $(SUBDIR)/vdso-be.so $(SUBDIR)/vdso-le.so
|
||||||
|
|
||||||
|
LDFLAGS = -nostdlib -shared -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
|
||||||
|
$(CC) -o $@ $(LDFLAGS) -mbig-endian $<
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso-le.so: vdso.S vdso.ld
|
||||||
|
$(CC) -o $@ $(LDFLAGS) -mlittle-endian $<
|
11
linux-user/aarch64/meson.build
Normal file
11
linux-user/aarch64/meson.build
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 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: ['-r', '__kernel_rt_sigreturn'])
|
||||||
|
|
||||||
|
vdso_le_inc = gen_vdso.process('vdso-le.so',
|
||||||
|
extra_args: ['-r', '__kernel_rt_sigreturn'])
|
||||||
|
|
||||||
|
linux_user_ss.add(when: 'TARGET_AARCH64', if_true: [vdso_be_inc, vdso_le_inc])
|
BIN
linux-user/aarch64/vdso-be.so
Executable file
BIN
linux-user/aarch64/vdso-be.so
Executable file
Binary file not shown.
BIN
linux-user/aarch64/vdso-le.so
Executable file
BIN
linux-user/aarch64/vdso-le.so
Executable file
Binary file not shown.
71
linux-user/aarch64/vdso.S
Normal file
71
linux-user/aarch64/vdso.S
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* aarch64 linux replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
/* ??? These are in include/elf.h, which is not ready for inclusion in asm. */
|
||||||
|
#define NT_GNU_PROPERTY_TYPE_0 5
|
||||||
|
#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000
|
||||||
|
#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0)
|
||||||
|
#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1)
|
||||||
|
|
||||||
|
#define GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT \
|
||||||
|
(GNU_PROPERTY_AARCH64_FEATURE_1_BTI | GNU_PROPERTY_AARCH64_FEATURE_1_PAC)
|
||||||
|
|
||||||
|
.section .note.gnu.property
|
||||||
|
.align 3
|
||||||
|
.long 2f - 1f
|
||||||
|
.long 6f - 3f
|
||||||
|
.long NT_GNU_PROPERTY_TYPE_0
|
||||||
|
1: .string "GNU"
|
||||||
|
2: .align 3
|
||||||
|
3: .long GNU_PROPERTY_AARCH64_FEATURE_1_AND
|
||||||
|
.long 5f - 4f
|
||||||
|
4: .long GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT
|
||||||
|
5: .align 3
|
||||||
|
6:
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
.macro endf name
|
||||||
|
.globl \name
|
||||||
|
.type \name, @function
|
||||||
|
.size \name, . - \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro vdso_syscall name, nr
|
||||||
|
\name:
|
||||||
|
bti c
|
||||||
|
mov x8, #\nr
|
||||||
|
svc #0
|
||||||
|
ret
|
||||||
|
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
|
||||||
|
|
||||||
|
.cfi_endproc
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: The kernel makes a big deal of turning off the .cfi directives,
|
||||||
|
* because they cause libgcc to crash, but that's because they're wrong.
|
||||||
|
*
|
||||||
|
* For now, elide the unwind info for __kernel_rt_sigreturn and rely on
|
||||||
|
* the libgcc fallback routine as we have always done. This requires
|
||||||
|
* that the code sequence used be exact.
|
||||||
|
*/
|
||||||
|
__kernel_rt_sigreturn:
|
||||||
|
/* No BTI C insn here -- we arrive via RET. */
|
||||||
|
mov x8, #__NR_rt_sigreturn
|
||||||
|
svc #0
|
||||||
|
endf __kernel_rt_sigreturn
|
72
linux-user/aarch64/vdso.ld
Normal file
72
linux-user/aarch64/vdso.ld
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for linux aarch64 replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
VERSION {
|
||||||
|
LINUX_2.6.39 {
|
||||||
|
global:
|
||||||
|
__kernel_rt_sigreturn;
|
||||||
|
__kernel_gettimeofday;
|
||||||
|
__kernel_clock_gettime;
|
||||||
|
__kernel_clock_getres;
|
||||||
|
|
||||||
|
local: *;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PHDRS {
|
||||||
|
phdr PT_PHDR FLAGS(4) PHDRS;
|
||||||
|
load PT_LOAD FLAGS(7) FILEHDR PHDRS;
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
|
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||||
|
note PT_NOTE FLAGS(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/*
|
||||||
|
* We can't prelink to any address without knowing something about
|
||||||
|
* the virtual memory space of the host, since that leaks over into
|
||||||
|
* the available memory space of the guest.
|
||||||
|
*/
|
||||||
|
. = 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 =0xd503201f
|
||||||
|
}
|
17
linux-user/arm/Makefile.vdso
Normal file
17
linux-user/arm/Makefile.vdso
Normal file
@ -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 $<
|
@ -5,3 +5,15 @@ syscall_nr_generators += {
|
|||||||
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
||||||
output: '@BASENAME@_nr.h')
|
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])
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "linux-user/trace.h"
|
#include "linux-user/trace.h"
|
||||||
#include "target/arm/cpu-features.h"
|
#include "target/arm/cpu-features.h"
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
struct target_sigcontext {
|
struct target_sigcontext {
|
||||||
abi_ulong trap_no;
|
abi_ulong trap_no;
|
||||||
@ -103,6 +104,11 @@ struct rt_sigframe
|
|||||||
struct sigframe sig;
|
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;
|
static abi_ptr sigreturn_fdpic_tramp;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -161,6 +167,9 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize)
|
|||||||
return (sp - framesize) & ~7;
|
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
|
static int
|
||||||
setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
|
setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
|
||||||
struct sigframe *frame, abi_ulong sp_addr)
|
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 = 0;
|
||||||
abi_ulong handler_fdpic_GOT = 0;
|
abi_ulong handler_fdpic_GOT = 0;
|
||||||
abi_ulong retcode;
|
abi_ulong retcode;
|
||||||
int thumb, retcode_idx;
|
bool is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info);
|
||||||
int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info);
|
bool is_rt = ka->sa_flags & TARGET_SA_SIGINFO;
|
||||||
bool copy_retcode;
|
bool thumb;
|
||||||
|
|
||||||
if (is_fdpic) {
|
if (is_fdpic) {
|
||||||
/* In FDPIC mode, ka->_sa_handler points to a function
|
/* 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 {
|
} else {
|
||||||
handler = ka->_sa_handler;
|
handler = ka->_sa_handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
thumb = handler & 1;
|
thumb = handler & 1;
|
||||||
retcode_idx = thumb + (ka->sa_flags & TARGET_SA_SIGINFO ? 2 : 0);
|
|
||||||
|
|
||||||
uint32_t cpsr = cpsr_read(env);
|
uint32_t cpsr = cpsr_read(env);
|
||||||
|
|
||||||
@ -203,24 +210,32 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
|
|||||||
cpsr &= ~CPSR_E;
|
cpsr &= ~CPSR_E;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
/* Our vdso default_sigreturn label is a table of entry points. */
|
||||||
if (is_fdpic) {
|
retcode = default_sigreturn + (is_fdpic * 2 + is_rt) * 8;
|
||||||
__put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]);
|
|
||||||
retcode = (sigreturn_fdpic_tramp +
|
/*
|
||||||
retcode_idx * RETCODE_BYTES + thumb);
|
* Put the sigreturn code on the stack no matter which return
|
||||||
copy_retcode = true;
|
* mechanism we use in order to remain ABI compliant.
|
||||||
} else {
|
* Because this is about ABI, always use the A32 instructions,
|
||||||
retcode = ka->sa_restorer;
|
* despite the fact that our actual vdso trampoline is T16.
|
||||||
copy_retcode = false;
|
*/
|
||||||
}
|
if (is_fdpic) {
|
||||||
|
write_arm_fdpic_sigreturn(frame->retcode,
|
||||||
|
is_rt ? RT_SIGFRAME_RC3_OFFSET
|
||||||
|
: SIGFRAME_RC3_OFFSET);
|
||||||
} else {
|
} else {
|
||||||
retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb;
|
write_arm_sigreturn(frame->retcode,
|
||||||
copy_retcode = true;
|
is_rt ? TARGET_NR_rt_sigreturn
|
||||||
|
: TARGET_NR_sigreturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the code to the stack slot for ABI compatibility. */
|
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
||||||
if (copy_retcode) {
|
if (is_fdpic) {
|
||||||
memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES);
|
/* Place the function descriptor in slot 3. */
|
||||||
|
__put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]);
|
||||||
|
} else {
|
||||||
|
retcode = ka->sa_restorer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env->regs[0] = usig;
|
env->regs[0] = usig;
|
||||||
|
3
linux-user/arm/vdso-asmoffset.h
Normal file
3
linux-user/arm/vdso-asmoffset.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/* offsetof(struct sigframe, retcode[3]) */
|
||||||
|
#define SIGFRAME_RC3_OFFSET 756
|
||||||
|
#define RT_SIGFRAME_RC3_OFFSET 884
|
BIN
linux-user/arm/vdso-be.so
Executable file
BIN
linux-user/arm/vdso-be.so
Executable file
Binary file not shown.
BIN
linux-user/arm/vdso-le.so
Executable file
BIN
linux-user/arm/vdso-le.so
Executable file
Binary file not shown.
174
linux-user/arm/vdso.S
Normal file
174
linux-user/arm/vdso.S
Normal file
@ -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
|
67
linux-user/arm/vdso.ld
Normal file
67
linux-user/arm/vdso.ld
Normal file
@ -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
|
||||||
|
}
|
@ -37,6 +37,19 @@
|
|||||||
#undef ELF_ARCH
|
#undef ELF_ARCH
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE
|
||||||
|
#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t *image;
|
||||||
|
const uint32_t *relocs;
|
||||||
|
unsigned image_size;
|
||||||
|
unsigned reloc_count;
|
||||||
|
unsigned sigreturn_ofs;
|
||||||
|
unsigned rt_sigreturn_ofs;
|
||||||
|
} VdsoImageInfo;
|
||||||
|
|
||||||
#define ELF_OSABI ELFOSABI_SYSV
|
#define ELF_OSABI ELFOSABI_SYSV
|
||||||
|
|
||||||
/* from personality.h */
|
/* from personality.h */
|
||||||
@ -296,12 +309,27 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en
|
|||||||
(*regs)[15] = tswapreg(env->regs[R_ESP]);
|
(*regs)[15] = tswapreg(env->regs[R_ESP]);
|
||||||
(*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff);
|
(*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)
|
||||||
|
|
||||||
|
#endif /* TARGET_X86_64 */
|
||||||
|
|
||||||
|
#define VDSO_HEADER "vdso.c.inc"
|
||||||
|
|
||||||
#define USE_ELF_CORE_DUMP
|
#define USE_ELF_CORE_DUMP
|
||||||
#define ELF_EXEC_PAGESIZE 4096
|
#define ELF_EXEC_PAGESIZE 4096
|
||||||
|
|
||||||
#endif
|
#endif /* TARGET_I386 */
|
||||||
|
|
||||||
#ifdef TARGET_ARM
|
#ifdef TARGET_ARM
|
||||||
|
|
||||||
@ -921,6 +949,13 @@ const char *elf_hwcap2_str(uint32_t bit)
|
|||||||
#undef GET_FEATURE_ID
|
#undef GET_FEATURE_ID
|
||||||
|
|
||||||
#endif /* not TARGET_AARCH64 */
|
#endif /* not TARGET_AARCH64 */
|
||||||
|
|
||||||
|
#if TARGET_BIG_ENDIAN
|
||||||
|
# define VDSO_HEADER "vdso-be.c.inc"
|
||||||
|
#else
|
||||||
|
# define VDSO_HEADER "vdso-le.c.inc"
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* TARGET_ARM */
|
#endif /* TARGET_ARM */
|
||||||
|
|
||||||
#ifdef TARGET_SPARC
|
#ifdef TARGET_SPARC
|
||||||
@ -1156,6 +1191,14 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en
|
|||||||
#define USE_ELF_CORE_DUMP
|
#define USE_ELF_CORE_DUMP
|
||||||
#define ELF_EXEC_PAGESIZE 4096
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef TARGET_LOONGARCH64
|
#ifdef TARGET_LOONGARCH64
|
||||||
@ -1166,6 +1209,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en
|
|||||||
|
|
||||||
#define elf_check_arch(x) ((x) == EM_LOONGARCH)
|
#define elf_check_arch(x) ((x) == EM_LOONGARCH)
|
||||||
|
|
||||||
|
#define VDSO_HEADER "vdso.c.inc"
|
||||||
|
|
||||||
static inline void init_thread(struct target_pt_regs *regs,
|
static inline void init_thread(struct target_pt_regs *regs,
|
||||||
struct image_info *infop)
|
struct image_info *infop)
|
||||||
{
|
{
|
||||||
@ -1853,6 +1898,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs,
|
|||||||
#define USE_ELF_CORE_DUMP
|
#define USE_ELF_CORE_DUMP
|
||||||
#define ELF_EXEC_PAGESIZE 4096
|
#define ELF_EXEC_PAGESIZE 4096
|
||||||
|
|
||||||
|
#define VDSO_HEADER "vdso.c.inc"
|
||||||
|
|
||||||
#endif /* TARGET_S390X */
|
#endif /* TARGET_S390X */
|
||||||
|
|
||||||
#ifdef TARGET_RISCV
|
#ifdef TARGET_RISCV
|
||||||
@ -1861,8 +1908,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs,
|
|||||||
|
|
||||||
#ifdef TARGET_RISCV32
|
#ifdef TARGET_RISCV32
|
||||||
#define ELF_CLASS ELFCLASS32
|
#define ELF_CLASS ELFCLASS32
|
||||||
|
#define VDSO_HEADER "vdso-32.c.inc"
|
||||||
#else
|
#else
|
||||||
#define ELF_CLASS ELFCLASS64
|
#define ELF_CLASS ELFCLASS64
|
||||||
|
#define VDSO_HEADER "vdso-64.c.inc"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ELF_HWCAP get_elf_hwcap()
|
#define ELF_HWCAP get_elf_hwcap()
|
||||||
@ -1898,6 +1947,8 @@ static inline void init_thread(struct target_pt_regs *regs,
|
|||||||
#define STACK_GROWS_DOWN 0
|
#define STACK_GROWS_DOWN 0
|
||||||
#define STACK_ALIGNMENT 64
|
#define STACK_ALIGNMENT 64
|
||||||
|
|
||||||
|
#define VDSO_HEADER "vdso.c.inc"
|
||||||
|
|
||||||
static inline void init_thread(struct target_pt_regs *regs,
|
static inline void init_thread(struct target_pt_regs *regs,
|
||||||
struct image_info *infop)
|
struct image_info *infop)
|
||||||
{
|
{
|
||||||
@ -2205,7 +2256,8 @@ static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { }
|
|||||||
#ifdef USE_ELF_CORE_DUMP
|
#ifdef USE_ELF_CORE_DUMP
|
||||||
static int elf_core_dump(int, const CPUArchState *);
|
static int elf_core_dump(int, const CPUArchState *);
|
||||||
#endif /* USE_ELF_CORE_DUMP */
|
#endif /* USE_ELF_CORE_DUMP */
|
||||||
static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias);
|
static void load_symbols(struct elfhdr *hdr, const ImageSource *src,
|
||||||
|
abi_ulong load_bias);
|
||||||
|
|
||||||
/* Verify the portions of EHDR within E_IDENT for the target.
|
/* Verify the portions of EHDR within E_IDENT for the target.
|
||||||
This can be performed before bswapping the entire header. */
|
This can be performed before bswapping the entire header. */
|
||||||
@ -2474,7 +2526,8 @@ static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong s
|
|||||||
static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
|
static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
|
||||||
struct elfhdr *exec,
|
struct elfhdr *exec,
|
||||||
struct image_info *info,
|
struct image_info *info,
|
||||||
struct image_info *interp_info)
|
struct image_info *interp_info,
|
||||||
|
struct image_info *vdso_info)
|
||||||
{
|
{
|
||||||
abi_ulong sp;
|
abi_ulong sp;
|
||||||
abi_ulong u_argc, u_argv, u_envp, u_auxv;
|
abi_ulong u_argc, u_argv, u_envp, u_auxv;
|
||||||
@ -2562,10 +2615,15 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size = (DLINFO_ITEMS + 1) * 2;
|
size = (DLINFO_ITEMS + 1) * 2;
|
||||||
if (k_base_platform)
|
if (k_base_platform) {
|
||||||
size += 2;
|
size += 2;
|
||||||
if (k_platform)
|
}
|
||||||
|
if (k_platform) {
|
||||||
size += 2;
|
size += 2;
|
||||||
|
}
|
||||||
|
if (vdso_info) {
|
||||||
|
size += 2;
|
||||||
|
}
|
||||||
#ifdef DLINFO_ARCH_ITEMS
|
#ifdef DLINFO_ARCH_ITEMS
|
||||||
size += DLINFO_ARCH_ITEMS * 2;
|
size += DLINFO_ARCH_ITEMS * 2;
|
||||||
#endif
|
#endif
|
||||||
@ -2647,6 +2705,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
|
|||||||
if (u_platform) {
|
if (u_platform) {
|
||||||
NEW_AUX_ENT(AT_PLATFORM, u_platform);
|
NEW_AUX_ENT(AT_PLATFORM, u_platform);
|
||||||
}
|
}
|
||||||
|
if (vdso_info) {
|
||||||
|
NEW_AUX_ENT(AT_SYSINFO_EHDR, vdso_info->load_addr);
|
||||||
|
}
|
||||||
NEW_AUX_ENT (AT_NULL, 0);
|
NEW_AUX_ENT (AT_NULL, 0);
|
||||||
#undef NEW_AUX_ENT
|
#undef NEW_AUX_ENT
|
||||||
|
|
||||||
@ -3106,10 +3167,9 @@ static bool parse_elf_property(const uint32_t *data, int *off, int datasz,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process NT_GNU_PROPERTY_TYPE_0. */
|
/* Process NT_GNU_PROPERTY_TYPE_0. */
|
||||||
static bool parse_elf_properties(int image_fd,
|
static bool parse_elf_properties(const ImageSource *src,
|
||||||
struct image_info *info,
|
struct image_info *info,
|
||||||
const struct elf_phdr *phdr,
|
const struct elf_phdr *phdr,
|
||||||
char bprm_buf[BPRM_BUF_SIZE],
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
@ -3137,14 +3197,8 @@ static bool parse_elf_properties(int image_fd,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phdr->p_offset + n <= BPRM_BUF_SIZE) {
|
if (!imgsrc_read(¬e, phdr->p_offset, n, src, errp)) {
|
||||||
memcpy(¬e, bprm_buf + phdr->p_offset, n);
|
return false;
|
||||||
} else {
|
|
||||||
ssize_t len = pread(image_fd, ¬e, n, phdr->p_offset);
|
|
||||||
if (len != n) {
|
|
||||||
error_setg_errno(errp, errno, "Error reading file header");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3190,29 +3244,34 @@ static bool parse_elf_properties(int image_fd,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load an ELF image into the address space.
|
/**
|
||||||
|
* load_elf_image: Load an ELF image into the address space.
|
||||||
|
* @image_name: the filename of the image, to use in error messages.
|
||||||
|
* @src: the ImageSource from which to read.
|
||||||
|
* @info: info collected from the loaded image.
|
||||||
|
* @ehdr: the ELF header, not yet bswapped.
|
||||||
|
* @pinterp_name: record any PT_INTERP string found.
|
||||||
|
*
|
||||||
|
* On return: @info values will be filled in, as necessary or available.
|
||||||
|
*/
|
||||||
|
|
||||||
IMAGE_NAME is the filename of the image, to use in error messages.
|
static void load_elf_image(const char *image_name, const ImageSource *src,
|
||||||
IMAGE_FD is the open file descriptor for the image.
|
struct image_info *info, struct elfhdr *ehdr,
|
||||||
|
char **pinterp_name)
|
||||||
BPRM_BUF is a copy of the beginning of the file; this of course
|
|
||||||
contains the elf file header at offset 0. It is assumed that this
|
|
||||||
buffer is sufficiently aligned to present no problems to the host
|
|
||||||
in accessing data at aligned offsets within the buffer.
|
|
||||||
|
|
||||||
On return: INFO values will be filled in, as necessary or available. */
|
|
||||||
|
|
||||||
static void load_elf_image(const char *image_name, int image_fd,
|
|
||||||
struct image_info *info, char **pinterp_name,
|
|
||||||
char bprm_buf[BPRM_BUF_SIZE])
|
|
||||||
{
|
{
|
||||||
struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
|
g_autofree struct elf_phdr *phdr = NULL;
|
||||||
struct elf_phdr *phdr;
|
|
||||||
abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
|
abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
|
||||||
int i, retval, prot_exec;
|
int i, prot_exec;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
/* First of all, some simple consistency checks */
|
/*
|
||||||
|
* First of all, some simple consistency checks.
|
||||||
|
* Note that we rely on the bswapped ehdr staying in bprm_buf,
|
||||||
|
* for later use by load_elf_binary and create_elf_tables.
|
||||||
|
*/
|
||||||
|
if (!imgsrc_read(ehdr, 0, sizeof(*ehdr), src, &err)) {
|
||||||
|
goto exit_errmsg;
|
||||||
|
}
|
||||||
if (!elf_check_ident(ehdr)) {
|
if (!elf_check_ident(ehdr)) {
|
||||||
error_setg(&err, "Invalid ELF image for this architecture");
|
error_setg(&err, "Invalid ELF image for this architecture");
|
||||||
goto exit_errmsg;
|
goto exit_errmsg;
|
||||||
@ -3223,15 +3282,11 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
goto exit_errmsg;
|
goto exit_errmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = ehdr->e_phnum * sizeof(struct elf_phdr);
|
phdr = imgsrc_read_alloc(ehdr->e_phoff,
|
||||||
if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) {
|
ehdr->e_phnum * sizeof(struct elf_phdr),
|
||||||
phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff);
|
src, &err);
|
||||||
} else {
|
if (phdr == NULL) {
|
||||||
phdr = (struct elf_phdr *) alloca(i);
|
goto exit_errmsg;
|
||||||
retval = pread(image_fd, phdr, i, ehdr->e_phoff);
|
|
||||||
if (retval != i) {
|
|
||||||
goto exit_read;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bswap_phdr(phdr, ehdr->e_phnum);
|
bswap_phdr(phdr, ehdr->e_phnum);
|
||||||
|
|
||||||
@ -3268,17 +3323,10 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
goto exit_errmsg;
|
goto exit_errmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
interp_name = g_malloc(eppnt->p_filesz);
|
interp_name = imgsrc_read_alloc(eppnt->p_offset, eppnt->p_filesz,
|
||||||
|
src, &err);
|
||||||
if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
|
if (interp_name == NULL) {
|
||||||
memcpy(interp_name, bprm_buf + eppnt->p_offset,
|
goto exit_errmsg;
|
||||||
eppnt->p_filesz);
|
|
||||||
} else {
|
|
||||||
retval = pread(image_fd, interp_name, eppnt->p_filesz,
|
|
||||||
eppnt->p_offset);
|
|
||||||
if (retval != eppnt->p_filesz) {
|
|
||||||
goto exit_read;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (interp_name[eppnt->p_filesz - 1] != 0) {
|
if (interp_name[eppnt->p_filesz - 1] != 0) {
|
||||||
error_setg(&err, "Invalid PT_INTERP entry");
|
error_setg(&err, "Invalid PT_INTERP entry");
|
||||||
@ -3286,7 +3334,7 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
}
|
}
|
||||||
*pinterp_name = g_steal_pointer(&interp_name);
|
*pinterp_name = g_steal_pointer(&interp_name);
|
||||||
} else if (eppnt->p_type == PT_GNU_PROPERTY) {
|
} else if (eppnt->p_type == PT_GNU_PROPERTY) {
|
||||||
if (!parse_elf_properties(image_fd, info, eppnt, bprm_buf, &err)) {
|
if (!parse_elf_properties(src, info, eppnt, &err)) {
|
||||||
goto exit_errmsg;
|
goto exit_errmsg;
|
||||||
}
|
}
|
||||||
} else if (eppnt->p_type == PT_GNU_STACK) {
|
} else if (eppnt->p_type == PT_GNU_STACK) {
|
||||||
@ -3439,9 +3487,9 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
* but no backing file segment.
|
* but no backing file segment.
|
||||||
*/
|
*/
|
||||||
if (eppnt->p_filesz != 0) {
|
if (eppnt->p_filesz != 0) {
|
||||||
error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
|
error = imgsrc_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
|
||||||
elf_prot, MAP_PRIVATE | MAP_FIXED,
|
elf_prot, MAP_PRIVATE | MAP_FIXED,
|
||||||
image_fd, eppnt->p_offset - vaddr_po);
|
src, eppnt->p_offset - vaddr_po);
|
||||||
if (error == -1) {
|
if (error == -1) {
|
||||||
goto exit_mmap;
|
goto exit_mmap;
|
||||||
}
|
}
|
||||||
@ -3473,20 +3521,11 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
#ifdef TARGET_MIPS
|
#ifdef TARGET_MIPS
|
||||||
} else if (eppnt->p_type == PT_MIPS_ABIFLAGS) {
|
} else if (eppnt->p_type == PT_MIPS_ABIFLAGS) {
|
||||||
Mips_elf_abiflags_v0 abiflags;
|
Mips_elf_abiflags_v0 abiflags;
|
||||||
if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) {
|
|
||||||
error_setg(&err, "Invalid PT_MIPS_ABIFLAGS entry");
|
if (!imgsrc_read(&abiflags, eppnt->p_offset, sizeof(abiflags),
|
||||||
|
src, &err)) {
|
||||||
goto exit_errmsg;
|
goto exit_errmsg;
|
||||||
}
|
}
|
||||||
if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
|
|
||||||
memcpy(&abiflags, bprm_buf + eppnt->p_offset,
|
|
||||||
sizeof(Mips_elf_abiflags_v0));
|
|
||||||
} else {
|
|
||||||
retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0),
|
|
||||||
eppnt->p_offset);
|
|
||||||
if (retval != sizeof(Mips_elf_abiflags_v0)) {
|
|
||||||
goto exit_read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bswap_mips_abiflags(&abiflags);
|
bswap_mips_abiflags(&abiflags);
|
||||||
info->fp_abi = abiflags.fp_abi;
|
info->fp_abi = abiflags.fp_abi;
|
||||||
#endif
|
#endif
|
||||||
@ -3499,23 +3538,16 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (qemu_log_enabled()) {
|
if (qemu_log_enabled()) {
|
||||||
load_symbols(ehdr, image_fd, load_bias);
|
load_symbols(ehdr, src, load_bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
debuginfo_report_elf(image_name, image_fd, load_bias);
|
debuginfo_report_elf(image_name, src->fd, load_bias);
|
||||||
|
|
||||||
mmap_unlock();
|
mmap_unlock();
|
||||||
|
|
||||||
close(image_fd);
|
close(src->fd);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
exit_read:
|
|
||||||
if (retval >= 0) {
|
|
||||||
error_setg(&err, "Incomplete read of file header");
|
|
||||||
} else {
|
|
||||||
error_setg_errno(&err, errno, "Error reading file header");
|
|
||||||
}
|
|
||||||
goto exit_errmsg;
|
|
||||||
exit_mmap:
|
exit_mmap:
|
||||||
error_setg_errno(&err, errno, "Error mapping file");
|
error_setg_errno(&err, errno, "Error mapping file");
|
||||||
goto exit_errmsg;
|
goto exit_errmsg;
|
||||||
@ -3527,6 +3559,8 @@ static void load_elf_image(const char *image_name, int image_fd,
|
|||||||
static void load_elf_interp(const char *filename, struct image_info *info,
|
static void load_elf_interp(const char *filename, struct image_info *info,
|
||||||
char bprm_buf[BPRM_BUF_SIZE])
|
char bprm_buf[BPRM_BUF_SIZE])
|
||||||
{
|
{
|
||||||
|
struct elfhdr ehdr;
|
||||||
|
ImageSource src;
|
||||||
int fd, retval;
|
int fd, retval;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
@ -3544,11 +3578,57 @@ static void load_elf_interp(const char *filename, struct image_info *info,
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retval < BPRM_BUF_SIZE) {
|
src.fd = fd;
|
||||||
memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
|
src.cache = bprm_buf;
|
||||||
|
src.cache_size = retval;
|
||||||
|
|
||||||
|
load_elf_image(filename, &src, info, &ehdr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VDSO_HEADER
|
||||||
|
#include VDSO_HEADER
|
||||||
|
#define vdso_image_info() &vdso_image_info
|
||||||
|
#else
|
||||||
|
#define vdso_image_info() NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso)
|
||||||
|
{
|
||||||
|
ImageSource src;
|
||||||
|
struct elfhdr ehdr;
|
||||||
|
abi_ulong load_bias, load_addr;
|
||||||
|
|
||||||
|
src.fd = -1;
|
||||||
|
src.cache = vdso->image;
|
||||||
|
src.cache_size = vdso->image_size;
|
||||||
|
|
||||||
|
load_elf_image("<internal-vdso>", &src, info, &ehdr, NULL);
|
||||||
|
load_addr = info->load_addr;
|
||||||
|
load_bias = info->load_bias;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to relocate the VDSO image. The one built into the kernel
|
||||||
|
* is built for a fixed address. The one built for QEMU is not, since
|
||||||
|
* that requires close control of the guest address space.
|
||||||
|
* We pre-processed the image to locate all of the addresses that need
|
||||||
|
* to be updated.
|
||||||
|
*/
|
||||||
|
for (unsigned i = 0, n = vdso->reloc_count; i < n; i++) {
|
||||||
|
abi_ulong *addr = g2h_untagged(load_addr + vdso->relocs[i]);
|
||||||
|
*addr = tswapal(tswapal(*addr) + load_bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
load_elf_image(filename, fd, info, NULL, bprm_buf);
|
/* Install signal trampolines, if present. */
|
||||||
|
if (vdso->sigreturn_ofs) {
|
||||||
|
default_sigreturn = load_addr + vdso->sigreturn_ofs;
|
||||||
|
}
|
||||||
|
if (vdso->rt_sigreturn_ofs) {
|
||||||
|
default_rt_sigreturn = load_addr + vdso->rt_sigreturn_ofs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove write from VDSO segment. */
|
||||||
|
target_mprotect(info->start_data, info->end_data - info->start_data,
|
||||||
|
PROT_READ | PROT_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int symfind(const void *s0, const void *s1)
|
static int symfind(const void *s0, const void *s1)
|
||||||
@ -3595,19 +3675,20 @@ static int symcmp(const void *s0, const void *s1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Best attempt to load symbols from this ELF object. */
|
/* Best attempt to load symbols from this ELF object. */
|
||||||
static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
|
static void load_symbols(struct elfhdr *hdr, const ImageSource *src,
|
||||||
|
abi_ulong load_bias)
|
||||||
{
|
{
|
||||||
int i, shnum, nsyms, sym_idx = 0, str_idx = 0;
|
int i, shnum, nsyms, sym_idx = 0, str_idx = 0;
|
||||||
uint64_t segsz;
|
g_autofree struct elf_shdr *shdr = NULL;
|
||||||
struct elf_shdr *shdr;
|
|
||||||
char *strings = NULL;
|
char *strings = NULL;
|
||||||
struct syminfo *s = NULL;
|
struct elf_sym *syms = NULL;
|
||||||
struct elf_sym *new_syms, *syms = NULL;
|
struct elf_sym *new_syms;
|
||||||
|
uint64_t segsz;
|
||||||
|
|
||||||
shnum = hdr->e_shnum;
|
shnum = hdr->e_shnum;
|
||||||
i = shnum * sizeof(struct elf_shdr);
|
shdr = imgsrc_read_alloc(hdr->e_shoff, shnum * sizeof(struct elf_shdr),
|
||||||
shdr = (struct elf_shdr *)alloca(i);
|
src, NULL);
|
||||||
if (pread(fd, shdr, i, hdr->e_shoff) != i) {
|
if (shdr == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3625,31 +3706,33 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
|
|||||||
|
|
||||||
found:
|
found:
|
||||||
/* Now know where the strtab and symtab are. Snarf them. */
|
/* Now know where the strtab and symtab are. Snarf them. */
|
||||||
s = g_try_new(struct syminfo, 1);
|
|
||||||
if (!s) {
|
|
||||||
goto give_up;
|
|
||||||
}
|
|
||||||
|
|
||||||
segsz = shdr[str_idx].sh_size;
|
segsz = shdr[str_idx].sh_size;
|
||||||
s->disas_strtab = strings = g_try_malloc(segsz);
|
strings = g_try_malloc(segsz);
|
||||||
if (!strings ||
|
if (!strings) {
|
||||||
pread(fd, strings, segsz, shdr[str_idx].sh_offset) != segsz) {
|
goto give_up;
|
||||||
|
}
|
||||||
|
if (!imgsrc_read(strings, shdr[str_idx].sh_offset, segsz, src, NULL)) {
|
||||||
goto give_up;
|
goto give_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
segsz = shdr[sym_idx].sh_size;
|
segsz = shdr[sym_idx].sh_size;
|
||||||
syms = g_try_malloc(segsz);
|
|
||||||
if (!syms || pread(fd, syms, segsz, shdr[sym_idx].sh_offset) != segsz) {
|
|
||||||
goto give_up;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segsz / sizeof(struct elf_sym) > INT_MAX) {
|
if (segsz / sizeof(struct elf_sym) > INT_MAX) {
|
||||||
/* Implausibly large symbol table: give up rather than ploughing
|
/*
|
||||||
* on with the number of symbols calculation overflowing
|
* Implausibly large symbol table: give up rather than ploughing
|
||||||
|
* on with the number of symbols calculation overflowing.
|
||||||
*/
|
*/
|
||||||
goto give_up;
|
goto give_up;
|
||||||
}
|
}
|
||||||
nsyms = segsz / sizeof(struct elf_sym);
|
nsyms = segsz / sizeof(struct elf_sym);
|
||||||
|
syms = g_try_malloc(segsz);
|
||||||
|
if (!syms) {
|
||||||
|
goto give_up;
|
||||||
|
}
|
||||||
|
if (!imgsrc_read(syms, shdr[sym_idx].sh_offset, segsz, src, NULL)) {
|
||||||
|
goto give_up;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < nsyms; ) {
|
for (i = 0; i < nsyms; ) {
|
||||||
bswap_sym(syms + i);
|
bswap_sym(syms + i);
|
||||||
/* Throw away entries which we do not need. */
|
/* Throw away entries which we do not need. */
|
||||||
@ -3674,10 +3757,12 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
|
|||||||
goto give_up;
|
goto give_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt to free the storage associated with the local symbols
|
/*
|
||||||
that we threw away. Whether or not this has any effect on the
|
* Attempt to free the storage associated with the local symbols
|
||||||
memory allocation depends on the malloc implementation and how
|
* that we threw away. Whether or not this has any effect on the
|
||||||
many symbols we managed to discard. */
|
* memory allocation depends on the malloc implementation and how
|
||||||
|
* many symbols we managed to discard.
|
||||||
|
*/
|
||||||
new_syms = g_try_renew(struct elf_sym, syms, nsyms);
|
new_syms = g_try_renew(struct elf_sym, syms, nsyms);
|
||||||
if (new_syms == NULL) {
|
if (new_syms == NULL) {
|
||||||
goto give_up;
|
goto give_up;
|
||||||
@ -3686,20 +3771,23 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
|
|||||||
|
|
||||||
qsort(syms, nsyms, sizeof(*syms), symcmp);
|
qsort(syms, nsyms, sizeof(*syms), symcmp);
|
||||||
|
|
||||||
s->disas_num_syms = nsyms;
|
{
|
||||||
#if ELF_CLASS == ELFCLASS32
|
struct syminfo *s = g_new(struct syminfo, 1);
|
||||||
s->disas_symtab.elf32 = syms;
|
|
||||||
#else
|
|
||||||
s->disas_symtab.elf64 = syms;
|
|
||||||
#endif
|
|
||||||
s->lookup_symbol = lookup_symbolxx;
|
|
||||||
s->next = syminfos;
|
|
||||||
syminfos = s;
|
|
||||||
|
|
||||||
|
s->disas_strtab = strings;
|
||||||
|
s->disas_num_syms = nsyms;
|
||||||
|
#if ELF_CLASS == ELFCLASS32
|
||||||
|
s->disas_symtab.elf32 = syms;
|
||||||
|
#else
|
||||||
|
s->disas_symtab.elf64 = syms;
|
||||||
|
#endif
|
||||||
|
s->lookup_symbol = lookup_symbolxx;
|
||||||
|
s->next = syminfos;
|
||||||
|
syminfos = s;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
give_up:
|
give_up:
|
||||||
g_free(s);
|
|
||||||
g_free(strings);
|
g_free(strings);
|
||||||
g_free(syms);
|
g_free(syms);
|
||||||
}
|
}
|
||||||
@ -3741,8 +3829,14 @@ uint32_t get_elf_eflags(int fd)
|
|||||||
|
|
||||||
int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
|
int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
|
||||||
{
|
{
|
||||||
struct image_info interp_info;
|
/*
|
||||||
struct elfhdr elf_ex;
|
* We need a copy of the elf header for passing to create_elf_tables.
|
||||||
|
* We will have overwritten the original when we re-use bprm->buf
|
||||||
|
* while loading the interpreter. Allocate the storage for this now
|
||||||
|
* and let elf_load_image do any swapping that may be required.
|
||||||
|
*/
|
||||||
|
struct elfhdr ehdr;
|
||||||
|
struct image_info interp_info, vdso_info;
|
||||||
char *elf_interpreter = NULL;
|
char *elf_interpreter = NULL;
|
||||||
char *scratch;
|
char *scratch;
|
||||||
|
|
||||||
@ -3751,13 +3845,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
|
|||||||
interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN;
|
interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
load_elf_image(bprm->filename, bprm->fd, info,
|
load_elf_image(bprm->filename, &bprm->src, info, &ehdr, &elf_interpreter);
|
||||||
&elf_interpreter, bprm->buf);
|
|
||||||
|
|
||||||
/* ??? We need a copy of the elf header for passing to create_elf_tables.
|
|
||||||
If we do nothing, we'll have overwritten this when we re-use bprm->buf
|
|
||||||
when we load the interpreter. */
|
|
||||||
elf_ex = *(struct elfhdr *)bprm->buf;
|
|
||||||
|
|
||||||
/* Do this so that we can load the interpreter, if need be. We will
|
/* Do this so that we can load the interpreter, if need be. We will
|
||||||
change some of these later */
|
change some of these later */
|
||||||
@ -3829,10 +3917,14 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: load a vdso, which would also contain the signal trampolines.
|
* Load a vdso if available, which will amongst other things contain the
|
||||||
* Otherwise, allocate a private page to hold them.
|
* signal trampolines. Otherwise, allocate a separate page for them.
|
||||||
*/
|
*/
|
||||||
if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) {
|
const VdsoImageInfo *vdso = vdso_image_info();
|
||||||
|
if (vdso) {
|
||||||
|
load_elf_vdso(&vdso_info, vdso);
|
||||||
|
info->vdso = vdso_info.load_bias;
|
||||||
|
} else if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) {
|
||||||
abi_long tramp_page = target_mmap(0, TARGET_PAGE_SIZE,
|
abi_long tramp_page = target_mmap(0, TARGET_PAGE_SIZE,
|
||||||
PROT_READ | PROT_WRITE,
|
PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||||
@ -3844,8 +3936,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
|
|||||||
target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC);
|
target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
|
bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, info,
|
||||||
info, (elf_interpreter ? &interp_info : NULL));
|
elf_interpreter ? &interp_info : NULL,
|
||||||
|
vdso ? &vdso_info : NULL);
|
||||||
info->start_stack = bprm->p;
|
info->start_stack = bprm->p;
|
||||||
|
|
||||||
/* If we have an interpreter, set that as the program's entry point.
|
/* If we have an interpreter, set that as the program's entry point.
|
||||||
|
@ -463,7 +463,7 @@ static int load_flat_file(struct linux_binprm * bprm,
|
|||||||
DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n");
|
DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n");
|
||||||
|
|
||||||
textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC,
|
textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC,
|
||||||
MAP_PRIVATE, bprm->fd, 0);
|
MAP_PRIVATE, bprm->src.fd, 0);
|
||||||
if (textpos == -1) {
|
if (textpos == -1) {
|
||||||
fprintf(stderr, "Unable to mmap process text\n");
|
fprintf(stderr, "Unable to mmap process text\n");
|
||||||
return -1;
|
return -1;
|
||||||
@ -490,7 +490,7 @@ static int load_flat_file(struct linux_binprm * bprm,
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
result = target_pread(bprm->fd, datapos,
|
result = target_pread(bprm->src.fd, datapos,
|
||||||
data_len + (relocs * sizeof(abi_ulong)),
|
data_len + (relocs * sizeof(abi_ulong)),
|
||||||
fpos);
|
fpos);
|
||||||
}
|
}
|
||||||
@ -540,10 +540,10 @@ static int load_flat_file(struct linux_binprm * bprm,
|
|||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
result = target_pread(bprm->fd, textpos,
|
result = target_pread(bprm->src.fd, textpos,
|
||||||
text_len, 0);
|
text_len, 0);
|
||||||
if (result >= 0) {
|
if (result >= 0) {
|
||||||
result = target_pread(bprm->fd, datapos,
|
result = target_pread(bprm->src.fd, datapos,
|
||||||
data_len + (relocs * sizeof(abi_ulong)),
|
data_len + (relocs * sizeof(abi_ulong)),
|
||||||
ntohl(hdr->data_start));
|
ntohl(hdr->data_start));
|
||||||
}
|
}
|
||||||
|
314
linux-user/gen-vdso-elfn.c.inc
Normal file
314
linux-user/gen-vdso-elfn.c.inc
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* Post-process a vdso elf image for inclusion into qemu.
|
||||||
|
* Elf size specialization.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr)
|
||||||
|
{
|
||||||
|
bswaps(&ehdr->e_type); /* Object file type */
|
||||||
|
bswaps(&ehdr->e_machine); /* Architecture */
|
||||||
|
bswaps(&ehdr->e_version); /* Object file version */
|
||||||
|
bswaps(&ehdr->e_entry); /* Entry point virtual address */
|
||||||
|
bswaps(&ehdr->e_phoff); /* Program header table file offset */
|
||||||
|
bswaps(&ehdr->e_shoff); /* Section header table file offset */
|
||||||
|
bswaps(&ehdr->e_flags); /* Processor-specific flags */
|
||||||
|
bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */
|
||||||
|
bswaps(&ehdr->e_phentsize); /* Program header table entry size */
|
||||||
|
bswaps(&ehdr->e_phnum); /* Program header table entry count */
|
||||||
|
bswaps(&ehdr->e_shentsize); /* Section header table entry size */
|
||||||
|
bswaps(&ehdr->e_shnum); /* Section header table entry count */
|
||||||
|
bswaps(&ehdr->e_shstrndx); /* Section header string table index */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elfN(bswap_phdr)(ElfN(Phdr) *phdr)
|
||||||
|
{
|
||||||
|
bswaps(&phdr->p_type); /* Segment type */
|
||||||
|
bswaps(&phdr->p_flags); /* Segment flags */
|
||||||
|
bswaps(&phdr->p_offset); /* Segment file offset */
|
||||||
|
bswaps(&phdr->p_vaddr); /* Segment virtual address */
|
||||||
|
bswaps(&phdr->p_paddr); /* Segment physical address */
|
||||||
|
bswaps(&phdr->p_filesz); /* Segment size in file */
|
||||||
|
bswaps(&phdr->p_memsz); /* Segment size in memory */
|
||||||
|
bswaps(&phdr->p_align); /* Segment alignment */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elfN(bswap_shdr)(ElfN(Shdr) *shdr)
|
||||||
|
{
|
||||||
|
bswaps(&shdr->sh_name);
|
||||||
|
bswaps(&shdr->sh_type);
|
||||||
|
bswaps(&shdr->sh_flags);
|
||||||
|
bswaps(&shdr->sh_addr);
|
||||||
|
bswaps(&shdr->sh_offset);
|
||||||
|
bswaps(&shdr->sh_size);
|
||||||
|
bswaps(&shdr->sh_link);
|
||||||
|
bswaps(&shdr->sh_info);
|
||||||
|
bswaps(&shdr->sh_addralign);
|
||||||
|
bswaps(&shdr->sh_entsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elfN(bswap_sym)(ElfN(Sym) *sym)
|
||||||
|
{
|
||||||
|
bswaps(&sym->st_name);
|
||||||
|
bswaps(&sym->st_value);
|
||||||
|
bswaps(&sym->st_size);
|
||||||
|
bswaps(&sym->st_shndx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elfN(bswap_dyn)(ElfN(Dyn) *dyn)
|
||||||
|
{
|
||||||
|
bswaps(&dyn->d_tag); /* Dynamic type tag */
|
||||||
|
bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx,
|
||||||
|
void *buf, bool need_bswap)
|
||||||
|
{
|
||||||
|
unsigned str_idx = shdr[sym_idx].sh_link;
|
||||||
|
ElfN(Sym) *sym = buf + shdr[sym_idx].sh_offset;
|
||||||
|
unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym);
|
||||||
|
const char *str = buf + shdr[str_idx].sh_offset;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < sym_n; ++i) {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (need_bswap) {
|
||||||
|
elfN(bswap_sym)(sym + i);
|
||||||
|
}
|
||||||
|
name = str + sym[i].st_name;
|
||||||
|
|
||||||
|
if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
|
||||||
|
sigreturn_addr = sym[i].st_value;
|
||||||
|
}
|
||||||
|
if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
|
||||||
|
rt_sigreturn_addr = sym[i].st_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void elfN(process)(FILE *outf, void *buf, bool need_bswap)
|
||||||
|
{
|
||||||
|
ElfN(Ehdr) *ehdr = buf;
|
||||||
|
ElfN(Phdr) *phdr;
|
||||||
|
ElfN(Shdr) *shdr;
|
||||||
|
unsigned phnum, shnum;
|
||||||
|
unsigned dynamic_ofs = 0;
|
||||||
|
unsigned dynamic_addr = 0;
|
||||||
|
unsigned symtab_idx = 0;
|
||||||
|
unsigned dynsym_idx = 0;
|
||||||
|
unsigned first_segsz = 0;
|
||||||
|
int errors = 0;
|
||||||
|
|
||||||
|
if (need_bswap) {
|
||||||
|
elfN(bswap_ehdr)(ehdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
phnum = ehdr->e_phnum;
|
||||||
|
phdr = buf + ehdr->e_phoff;
|
||||||
|
if (need_bswap) {
|
||||||
|
for (unsigned i = 0; i < phnum; ++i) {
|
||||||
|
elfN(bswap_phdr)(phdr + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shnum = ehdr->e_shnum;
|
||||||
|
shdr = buf + ehdr->e_shoff;
|
||||||
|
if (need_bswap) {
|
||||||
|
for (unsigned i = 0; i < shnum; ++i) {
|
||||||
|
elfN(bswap_shdr)(shdr + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < shnum; ++i) {
|
||||||
|
switch (shdr[i].sh_type) {
|
||||||
|
case SHT_SYMTAB:
|
||||||
|
symtab_idx = i;
|
||||||
|
break;
|
||||||
|
case SHT_DYNSYM:
|
||||||
|
dynsym_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate the VDSO is created as we expect: that PT_PHDR,
|
||||||
|
* PT_DYNAMIC, and PT_NOTE located in a writable data segment.
|
||||||
|
* PHDR and DYNAMIC require relocation, and NOTE will get the
|
||||||
|
* linux version number.
|
||||||
|
*/
|
||||||
|
for (unsigned i = 0; i < phnum; ++i) {
|
||||||
|
if (phdr[i].p_type != PT_LOAD) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (first_segsz != 0) {
|
||||||
|
fprintf(stderr, "Multiple LOAD segments\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (phdr[i].p_offset != 0) {
|
||||||
|
fprintf(stderr, "LOAD segment does not cover EHDR\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (phdr[i].p_vaddr != 0) {
|
||||||
|
fprintf(stderr, "LOAD segment not loaded at address 0\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
first_segsz = phdr[i].p_filesz;
|
||||||
|
if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) {
|
||||||
|
fprintf(stderr, "LOAD segment does not cover PHDRs\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) {
|
||||||
|
fprintf(stderr, "LOAD segment is not read-write\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < phnum; ++i) {
|
||||||
|
const char *which;
|
||||||
|
|
||||||
|
switch (phdr[i].p_type) {
|
||||||
|
case PT_PHDR:
|
||||||
|
which = "PT_PHDR";
|
||||||
|
break;
|
||||||
|
case PT_NOTE:
|
||||||
|
which = "PT_NOTE";
|
||||||
|
break;
|
||||||
|
case PT_DYNAMIC:
|
||||||
|
dynamic_ofs = phdr[i].p_offset;
|
||||||
|
dynamic_addr = phdr[i].p_vaddr;
|
||||||
|
which = "PT_DYNAMIC";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) {
|
||||||
|
fprintf(stderr, "LOAD segment does not cover %s\n", which);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errors) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Relocate the program headers. */
|
||||||
|
for (unsigned i = 0; i < phnum; ++i) {
|
||||||
|
output_reloc(outf, buf, &phdr[i].p_vaddr);
|
||||||
|
output_reloc(outf, buf, &phdr[i].p_paddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Relocate the DYNAMIC entries. */
|
||||||
|
if (dynamic_addr) {
|
||||||
|
ElfN(Dyn) *dyn = buf + dynamic_ofs;
|
||||||
|
__typeof(dyn->d_tag) tag;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if (need_bswap) {
|
||||||
|
elfN(bswap_dyn)(dyn);
|
||||||
|
}
|
||||||
|
tag = dyn->d_tag;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case DT_HASH:
|
||||||
|
case DT_SYMTAB:
|
||||||
|
case DT_STRTAB:
|
||||||
|
case DT_VERDEF:
|
||||||
|
case DT_VERSYM:
|
||||||
|
case DT_PLTGOT:
|
||||||
|
case DT_ADDRRNGLO ... DT_ADDRRNGHI:
|
||||||
|
/* These entries store an address in the entry. */
|
||||||
|
output_reloc(outf, buf, &dyn->d_un.d_val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DT_NULL:
|
||||||
|
case DT_STRSZ:
|
||||||
|
case DT_SONAME:
|
||||||
|
case DT_DEBUG:
|
||||||
|
case DT_FLAGS:
|
||||||
|
case DT_FLAGS_1:
|
||||||
|
case DT_SYMBOLIC:
|
||||||
|
case DT_BIND_NOW:
|
||||||
|
case DT_VERDEFNUM:
|
||||||
|
case DT_VALRNGLO ... DT_VALRNGHI:
|
||||||
|
/* These entries store an integer in the entry. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DT_SYMENT:
|
||||||
|
if (dyn->d_un.d_val != sizeof(ElfN(Sym))) {
|
||||||
|
fprintf(stderr, "VDSO has incorrect dynamic symbol size\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DT_REL:
|
||||||
|
case DT_RELSZ:
|
||||||
|
case DT_RELA:
|
||||||
|
case DT_RELASZ:
|
||||||
|
/*
|
||||||
|
* These entries indicate that the VDSO was built incorrectly.
|
||||||
|
* It should not have any real relocations.
|
||||||
|
* ??? The RISC-V toolchain will emit these even when there
|
||||||
|
* are no relocations. Validate zeros.
|
||||||
|
*/
|
||||||
|
if (dyn->d_un.d_val != 0) {
|
||||||
|
fprintf(stderr, "VDSO has dynamic relocations\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DT_RELENT:
|
||||||
|
case DT_RELAENT:
|
||||||
|
case DT_TEXTREL:
|
||||||
|
/* These entries store an integer in the entry. */
|
||||||
|
/* Should not be required; see above. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DT_NEEDED:
|
||||||
|
case DT_VERNEED:
|
||||||
|
case DT_PLTREL:
|
||||||
|
case DT_JMPREL:
|
||||||
|
case DT_RPATH:
|
||||||
|
case DT_RUNPATH:
|
||||||
|
fprintf(stderr, "VDSO has external dependencies\n");
|
||||||
|
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);
|
||||||
|
errors++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dyn++;
|
||||||
|
} while (tag != DT_NULL);
|
||||||
|
if (errors) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Relocate the dynamic symbol table. */
|
||||||
|
if (dynsym_idx) {
|
||||||
|
ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset;
|
||||||
|
unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < sym_n; ++i) {
|
||||||
|
output_reloc(outf, buf, &sym[i].st_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search both dynsym and symtab for the signal return symbols. */
|
||||||
|
if (dynsym_idx) {
|
||||||
|
elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap);
|
||||||
|
}
|
||||||
|
if (symtab_idx) {
|
||||||
|
elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap);
|
||||||
|
}
|
||||||
|
}
|
223
linux-user/gen-vdso.c
Normal file
223
linux-user/gen-vdso.c
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Post-process a vdso elf image for inclusion into qemu.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define bswap_(p) _Generic(*(p), \
|
||||||
|
uint16_t: __builtin_bswap16, \
|
||||||
|
uint32_t: __builtin_bswap32, \
|
||||||
|
uint64_t: __builtin_bswap64, \
|
||||||
|
int16_t: __builtin_bswap16, \
|
||||||
|
int32_t: __builtin_bswap32, \
|
||||||
|
int64_t: __builtin_bswap64)
|
||||||
|
#define bswaps(p) (*(p) = bswap_(p)(*(p)))
|
||||||
|
|
||||||
|
static void output_reloc(FILE *outf, void *buf, void *loc)
|
||||||
|
{
|
||||||
|
fprintf(outf, " 0x%08tx,\n", loc - buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *sigreturn_sym;
|
||||||
|
static const char *rt_sigreturn_sym;
|
||||||
|
|
||||||
|
static unsigned sigreturn_addr;
|
||||||
|
static unsigned rt_sigreturn_addr;
|
||||||
|
|
||||||
|
#define N 32
|
||||||
|
#define elfN(x) elf32_##x
|
||||||
|
#define ElfN(x) Elf32_##x
|
||||||
|
#include "gen-vdso-elfn.c.inc"
|
||||||
|
#undef N
|
||||||
|
#undef elfN
|
||||||
|
#undef ElfN
|
||||||
|
|
||||||
|
#define N 64
|
||||||
|
#define elfN(x) elf64_##x
|
||||||
|
#define ElfN(x) Elf64_##x
|
||||||
|
#include "gen-vdso-elfn.c.inc"
|
||||||
|
#undef N
|
||||||
|
#undef elfN
|
||||||
|
#undef ElfN
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
FILE *inf, *outf;
|
||||||
|
long total_len;
|
||||||
|
const char *prefix = "vdso";
|
||||||
|
const char *inf_name;
|
||||||
|
const char *outf_name = NULL;
|
||||||
|
unsigned char *buf;
|
||||||
|
bool need_bswap;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int opt = getopt(argc, argv, "o:p:r:s:");
|
||||||
|
if (opt < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (opt) {
|
||||||
|
case 'o':
|
||||||
|
outf_name = optarg;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
prefix = optarg;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
rt_sigreturn_sym = optarg;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
sigreturn_sym = optarg;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage:
|
||||||
|
fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] "
|
||||||
|
"[-s sigreturn-name] -o output-file input-file\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc || outf_name == NULL) {
|
||||||
|
goto usage;
|
||||||
|
}
|
||||||
|
inf_name = argv[optind];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the input and output files.
|
||||||
|
*/
|
||||||
|
inf = fopen(inf_name, "rb");
|
||||||
|
if (inf == NULL) {
|
||||||
|
goto perror_inf;
|
||||||
|
}
|
||||||
|
outf = fopen(outf_name, "w");
|
||||||
|
if (outf == NULL) {
|
||||||
|
goto perror_outf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the input file into a buffer.
|
||||||
|
* We expect the vdso to be small, on the order of one page,
|
||||||
|
* therefore we do not expect a partial read.
|
||||||
|
*/
|
||||||
|
fseek(inf, 0, SEEK_END);
|
||||||
|
total_len = ftell(inf);
|
||||||
|
fseek(inf, 0, SEEK_SET);
|
||||||
|
|
||||||
|
buf = malloc(total_len);
|
||||||
|
if (buf == NULL) {
|
||||||
|
goto perror_inf;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (fread(buf, 1, total_len, inf) != total_len) {
|
||||||
|
if (errno) {
|
||||||
|
goto perror_inf;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%s: incomplete read\n", inf_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
fclose(inf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out the vdso image now, before we make local changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fprintf(outf,
|
||||||
|
"/* Automatically generated from linux-user/gen-vdso.c. */\n"
|
||||||
|
"\n"
|
||||||
|
"static const uint8_t %s_image[] = {",
|
||||||
|
prefix);
|
||||||
|
for (long i = 0; i < total_len; ++i) {
|
||||||
|
if (i % 12 == 0) {
|
||||||
|
fputs("\n ", outf);
|
||||||
|
}
|
||||||
|
fprintf(outf, " 0x%02x,", buf[i]);
|
||||||
|
}
|
||||||
|
fprintf(outf, "\n};\n\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Identify which elf flavor we're processing.
|
||||||
|
* The first 16 bytes of the file are e_ident.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 ||
|
||||||
|
buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) {
|
||||||
|
fprintf(stderr, "%s: not an elf file\n", inf_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
switch (buf[EI_DATA]) {
|
||||||
|
case ELFDATA2LSB:
|
||||||
|
need_bswap = BYTE_ORDER != LITTLE_ENDIAN;
|
||||||
|
break;
|
||||||
|
case ELFDATA2MSB:
|
||||||
|
need_bswap = BYTE_ORDER != BIG_ENDIAN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n",
|
||||||
|
inf_name, buf[EI_DATA]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to relocate the VDSO image. The one built into the kernel
|
||||||
|
* is built for a fixed address. The one we built for QEMU is not,
|
||||||
|
* since that requires close control of the guest address space.
|
||||||
|
*
|
||||||
|
* Output relocation addresses as we go.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fprintf(outf, "static const unsigned %s_relocs[] = {\n", prefix);
|
||||||
|
|
||||||
|
switch (buf[EI_CLASS]) {
|
||||||
|
case ELFCLASS32:
|
||||||
|
elf32_process(outf, buf, need_bswap);
|
||||||
|
break;
|
||||||
|
case ELFCLASS64:
|
||||||
|
elf64_process(outf, buf, need_bswap);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n",
|
||||||
|
inf_name, buf[EI_CLASS]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(outf, "};\n\n"); /* end vdso_relocs. */
|
||||||
|
|
||||||
|
fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix);
|
||||||
|
fprintf(outf, " .image = %s_image,\n", prefix);
|
||||||
|
fprintf(outf, " .relocs = %s_relocs,\n", prefix);
|
||||||
|
fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix);
|
||||||
|
fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix);
|
||||||
|
fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr);
|
||||||
|
fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr);
|
||||||
|
fprintf(outf, "};\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Everything should have gone well.
|
||||||
|
*/
|
||||||
|
if (fclose(outf)) {
|
||||||
|
goto perror_outf;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
perror_inf:
|
||||||
|
perror(inf_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
perror_outf:
|
||||||
|
perror(outf_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
11
linux-user/hppa/Makefile.vdso
Normal file
11
linux-user/hppa/Makefile.vdso
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
include $(BUILD_DIR)/tests/tcg/hppa-linux-user/config-target.mak
|
||||||
|
|
||||||
|
SUBDIR = $(SRC_PATH)/linux-user/hppa
|
||||||
|
VPATH += $(SUBDIR)
|
||||||
|
|
||||||
|
all: $(SUBDIR)/vdso.so
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h
|
||||||
|
$(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso32.so.1 \
|
||||||
|
-Wl,--build-id=sha1 -Wl,--hash-style=both \
|
||||||
|
-Wl,-T,$(SUBDIR)/vdso.ld $<
|
@ -3,3 +3,8 @@ syscall_nr_generators += {
|
|||||||
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
||||||
output: '@BASENAME@_nr.h')
|
output: '@BASENAME@_nr.h')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vdso_inc = gen_vdso.process('vdso.so',
|
||||||
|
extra_args: [ '-r', '__kernel_sigtramp_rt' ])
|
||||||
|
|
||||||
|
linux_user_ss.add(when: 'TARGET_HPPA', if_true: vdso_inc)
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "linux-user/trace.h"
|
#include "linux-user/trace.h"
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
struct target_sigcontext {
|
struct target_sigcontext {
|
||||||
abi_ulong sc_flags;
|
abi_ulong sc_flags;
|
||||||
@ -47,6 +48,19 @@ struct target_rt_sigframe {
|
|||||||
/* hidden location of upper halves of pa2.0 64-bit gregs */
|
/* hidden location of upper halves of pa2.0 64-bit gregs */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) != sizeof_rt_sigframe);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext)
|
||||||
|
!= offsetof_sigcontext);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_gr)
|
||||||
|
!= offsetof_sigcontext_gr);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_fr)
|
||||||
|
!= offsetof_sigcontext_fr);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_iaoq)
|
||||||
|
!= offsetof_sigcontext_iaoq);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_sar)
|
||||||
|
!= offsetof_sigcontext_sar);
|
||||||
|
|
||||||
|
|
||||||
static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env)
|
static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -91,16 +105,6 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc)
|
|||||||
__get_user(env->cr[CR_SAR], &sc->sc_sar);
|
__get_user(env->cr[CR_SAR], &sc->sc_sar);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_ABI_BITS == 32
|
|
||||||
#define SIGFRAME 64
|
|
||||||
#define FUNCTIONCALLFRAME 48
|
|
||||||
#else
|
|
||||||
#define SIGFRAME 128
|
|
||||||
#define FUNCTIONCALLFRAME 96
|
|
||||||
#endif
|
|
||||||
#define PARISC_RT_SIGFRAME_SIZE32 \
|
|
||||||
((sizeof(struct target_rt_sigframe) + FUNCTIONCALLFRAME + SIGFRAME) & -SIGFRAME)
|
|
||||||
|
|
||||||
void setup_rt_frame(int sig, struct target_sigaction *ka,
|
void setup_rt_frame(int sig, struct target_sigaction *ka,
|
||||||
target_siginfo_t *info,
|
target_siginfo_t *info,
|
||||||
target_sigset_t *set, CPUArchState *env)
|
target_sigset_t *set, CPUArchState *env)
|
||||||
|
12
linux-user/hppa/vdso-asmoffset.h
Normal file
12
linux-user/hppa/vdso-asmoffset.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#define sizeof_rt_sigframe 584
|
||||||
|
#define offsetof_sigcontext 160
|
||||||
|
#define offsetof_sigcontext_gr 0x4
|
||||||
|
#define offsetof_sigcontext_fr 0x88
|
||||||
|
#define offsetof_sigcontext_iaoq 0x190
|
||||||
|
#define offsetof_sigcontext_sar 0x198
|
||||||
|
|
||||||
|
/* arch/parisc/include/asm/rt_sigframe.h */
|
||||||
|
#define SIGFRAME 64
|
||||||
|
#define FUNCTIONCALLFRAME 48
|
||||||
|
#define PARISC_RT_SIGFRAME_SIZE32 \
|
||||||
|
(((sizeof_rt_sigframe) + FUNCTIONCALLFRAME + SIGFRAME) & -SIGFRAME)
|
165
linux-user/hppa/vdso.S
Normal file
165
linux-user/hppa/vdso.S
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* hppa linux kernel vdso replacement.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* arch/parisc/kernel/vdso32/sigtramp.S:
|
||||||
|
* Gdb expects the trampoline is on the stack and the pc is offset from
|
||||||
|
* a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline
|
||||||
|
* is not on the stack, we need a new variant with different offsets and
|
||||||
|
* data to tell gdb where to find the signal context on the stack.
|
||||||
|
*
|
||||||
|
* Here we put the offset to the context data at the start of the trampoline
|
||||||
|
* region and offset the first trampoline by 2 instructions. Please do
|
||||||
|
* not change the trampoline as the code in gdb depends on the following
|
||||||
|
* instruction sequence exactly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* arch/parisc/kernel/asm-offsets.c */
|
||||||
|
#define SIGFRAME_CONTEXT_REGS32 \
|
||||||
|
(offsetof_sigcontext - PARISC_RT_SIGFRAME_SIZE32)
|
||||||
|
|
||||||
|
.align 64
|
||||||
|
.word SIGFRAME_CONTEXT_REGS32
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All that said, we can provide a proper unwind record, which means that
|
||||||
|
* GDB should not actually need the offset magic.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.cfi_startproc simple
|
||||||
|
.cfi_signal_frame
|
||||||
|
|
||||||
|
/* Compare pa32_fallback_frame_state from libgcc. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Place the CFA at the start of sigcontext for convenience.
|
||||||
|
* The previous CFA will be restored from the saved stack pointer.
|
||||||
|
*/
|
||||||
|
.cfi_def_cfa 30, -PARISC_RT_SIGFRAME_SIZE32 + offsetof_sigcontext
|
||||||
|
|
||||||
|
/* Record save offset of general registers. */
|
||||||
|
.cfi_offset 1, offsetof_sigcontext_gr + 1 * 4
|
||||||
|
.cfi_offset 2, offsetof_sigcontext_gr + 2 * 4
|
||||||
|
.cfi_offset 3, offsetof_sigcontext_gr + 3 * 4
|
||||||
|
.cfi_offset 4, offsetof_sigcontext_gr + 4 * 4
|
||||||
|
.cfi_offset 5, offsetof_sigcontext_gr + 5 * 4
|
||||||
|
.cfi_offset 6, offsetof_sigcontext_gr + 6 * 4
|
||||||
|
.cfi_offset 7, offsetof_sigcontext_gr + 7 * 4
|
||||||
|
.cfi_offset 8, offsetof_sigcontext_gr + 8 * 4
|
||||||
|
.cfi_offset 9, offsetof_sigcontext_gr + 9 * 4
|
||||||
|
.cfi_offset 10, offsetof_sigcontext_gr + 10 * 4
|
||||||
|
.cfi_offset 11, offsetof_sigcontext_gr + 11 * 4
|
||||||
|
.cfi_offset 12, offsetof_sigcontext_gr + 12 * 4
|
||||||
|
.cfi_offset 13, offsetof_sigcontext_gr + 13 * 4
|
||||||
|
.cfi_offset 14, offsetof_sigcontext_gr + 14 * 4
|
||||||
|
.cfi_offset 15, offsetof_sigcontext_gr + 15 * 4
|
||||||
|
.cfi_offset 16, offsetof_sigcontext_gr + 16 * 4
|
||||||
|
.cfi_offset 17, offsetof_sigcontext_gr + 17 * 4
|
||||||
|
.cfi_offset 18, offsetof_sigcontext_gr + 18 * 4
|
||||||
|
.cfi_offset 19, offsetof_sigcontext_gr + 19 * 4
|
||||||
|
.cfi_offset 20, offsetof_sigcontext_gr + 20 * 4
|
||||||
|
.cfi_offset 21, offsetof_sigcontext_gr + 21 * 4
|
||||||
|
.cfi_offset 22, offsetof_sigcontext_gr + 22 * 4
|
||||||
|
.cfi_offset 23, offsetof_sigcontext_gr + 23 * 4
|
||||||
|
.cfi_offset 24, offsetof_sigcontext_gr + 24 * 4
|
||||||
|
.cfi_offset 25, offsetof_sigcontext_gr + 25 * 4
|
||||||
|
.cfi_offset 26, offsetof_sigcontext_gr + 26 * 4
|
||||||
|
.cfi_offset 27, offsetof_sigcontext_gr + 27 * 4
|
||||||
|
.cfi_offset 28, offsetof_sigcontext_gr + 28 * 4
|
||||||
|
.cfi_offset 29, offsetof_sigcontext_gr + 29 * 4
|
||||||
|
.cfi_offset 30, offsetof_sigcontext_gr + 30 * 4
|
||||||
|
.cfi_offset 31, offsetof_sigcontext_gr + 31 * 4
|
||||||
|
|
||||||
|
/* Record save offset of fp registers, left and right halves. */
|
||||||
|
.cfi_offset 32, offsetof_sigcontext_fr + 4 * 8
|
||||||
|
.cfi_offset 33, offsetof_sigcontext_fr + 4 * 8 + 4
|
||||||
|
.cfi_offset 34, offsetof_sigcontext_fr + 5 * 8
|
||||||
|
.cfi_offset 35, offsetof_sigcontext_fr + 5 * 8 + 4
|
||||||
|
.cfi_offset 36, offsetof_sigcontext_fr + 6 * 8
|
||||||
|
.cfi_offset 37, offsetof_sigcontext_fr + 6 * 8 + 4
|
||||||
|
.cfi_offset 38, offsetof_sigcontext_fr + 7 * 8
|
||||||
|
.cfi_offset 39, offsetof_sigcontext_fr + 7 * 8 + 4
|
||||||
|
.cfi_offset 40, offsetof_sigcontext_fr + 8 * 8
|
||||||
|
.cfi_offset 41, offsetof_sigcontext_fr + 8 * 8 + 4
|
||||||
|
.cfi_offset 42, offsetof_sigcontext_fr + 9 * 8
|
||||||
|
.cfi_offset 43, offsetof_sigcontext_fr + 9 * 8 + 4
|
||||||
|
.cfi_offset 44, offsetof_sigcontext_fr + 10 * 8
|
||||||
|
.cfi_offset 45, offsetof_sigcontext_fr + 10 * 8 + 4
|
||||||
|
.cfi_offset 46, offsetof_sigcontext_fr + 11 * 8
|
||||||
|
.cfi_offset 47, offsetof_sigcontext_fr + 11 * 8 + 4
|
||||||
|
.cfi_offset 48, offsetof_sigcontext_fr + 12 * 8
|
||||||
|
.cfi_offset 49, offsetof_sigcontext_fr + 12 * 8 + 4
|
||||||
|
.cfi_offset 50, offsetof_sigcontext_fr + 13 * 8
|
||||||
|
.cfi_offset 51, offsetof_sigcontext_fr + 13 * 8 + 4
|
||||||
|
.cfi_offset 52, offsetof_sigcontext_fr + 14 * 8
|
||||||
|
.cfi_offset 53, offsetof_sigcontext_fr + 14 * 8 + 4
|
||||||
|
.cfi_offset 54, offsetof_sigcontext_fr + 15 * 8
|
||||||
|
.cfi_offset 55, offsetof_sigcontext_fr + 15 * 8 + 4
|
||||||
|
.cfi_offset 56, offsetof_sigcontext_fr + 16 * 8
|
||||||
|
.cfi_offset 57, offsetof_sigcontext_fr + 16 * 8 + 4
|
||||||
|
.cfi_offset 58, offsetof_sigcontext_fr + 17 * 8
|
||||||
|
.cfi_offset 59, offsetof_sigcontext_fr + 17 * 8 + 4
|
||||||
|
.cfi_offset 60, offsetof_sigcontext_fr + 18 * 8
|
||||||
|
.cfi_offset 61, offsetof_sigcontext_fr + 18 * 8 + 4
|
||||||
|
.cfi_offset 62, offsetof_sigcontext_fr + 19 * 8
|
||||||
|
.cfi_offset 63, offsetof_sigcontext_fr + 19 * 8 + 4
|
||||||
|
.cfi_offset 64, offsetof_sigcontext_fr + 20 * 8
|
||||||
|
.cfi_offset 65, offsetof_sigcontext_fr + 20 * 8 + 4
|
||||||
|
.cfi_offset 66, offsetof_sigcontext_fr + 21 * 8
|
||||||
|
.cfi_offset 67, offsetof_sigcontext_fr + 21 * 8 + 4
|
||||||
|
.cfi_offset 68, offsetof_sigcontext_fr + 22 * 8
|
||||||
|
.cfi_offset 69, offsetof_sigcontext_fr + 22 * 8 + 4
|
||||||
|
.cfi_offset 70, offsetof_sigcontext_fr + 23 * 8
|
||||||
|
.cfi_offset 71, offsetof_sigcontext_fr + 23 * 8 + 4
|
||||||
|
.cfi_offset 72, offsetof_sigcontext_fr + 24 * 8
|
||||||
|
.cfi_offset 73, offsetof_sigcontext_fr + 24 * 8 + 4
|
||||||
|
.cfi_offset 74, offsetof_sigcontext_fr + 25 * 8
|
||||||
|
.cfi_offset 75, offsetof_sigcontext_fr + 25 * 8 + 4
|
||||||
|
.cfi_offset 76, offsetof_sigcontext_fr + 26 * 8
|
||||||
|
.cfi_offset 77, offsetof_sigcontext_fr + 26 * 8 + 4
|
||||||
|
.cfi_offset 78, offsetof_sigcontext_fr + 27 * 8
|
||||||
|
.cfi_offset 79, offsetof_sigcontext_fr + 27 * 8 + 4
|
||||||
|
.cfi_offset 80, offsetof_sigcontext_fr + 28 * 8
|
||||||
|
.cfi_offset 81, offsetof_sigcontext_fr + 28 * 8 + 4
|
||||||
|
.cfi_offset 82, offsetof_sigcontext_fr + 29 * 8
|
||||||
|
.cfi_offset 83, offsetof_sigcontext_fr + 29 * 8 + 4
|
||||||
|
.cfi_offset 84, offsetof_sigcontext_fr + 30 * 8
|
||||||
|
.cfi_offset 85, offsetof_sigcontext_fr + 30 * 8 + 4
|
||||||
|
.cfi_offset 86, offsetof_sigcontext_fr + 31 * 8
|
||||||
|
.cfi_offset 87, offsetof_sigcontext_fr + 31 * 8 + 4
|
||||||
|
|
||||||
|
/* Record save offset of %sar */
|
||||||
|
.cfi_offset 88, offsetof_sigcontext_sar
|
||||||
|
|
||||||
|
/* Record save offset of return address, iaoq[0]. */
|
||||||
|
.cfi_return_column 89
|
||||||
|
.cfi_offset 89, offsetof_sigcontext_iaoq
|
||||||
|
|
||||||
|
nop
|
||||||
|
|
||||||
|
__kernel_sigtramp_rt:
|
||||||
|
ldi 0, %r25
|
||||||
|
ldi __NR_rt_sigreturn, %r20
|
||||||
|
be,l 0x100(%sr2, %r0), %sr0, %r31
|
||||||
|
nop
|
||||||
|
|
||||||
|
.cfi_endproc
|
||||||
|
.size __kernel_sigtramp_rt, . - __kernel_sigtramp_rt
|
||||||
|
.type __kernel_sigtramp_rt, @function
|
||||||
|
.globl __kernel_sigtramp_rt
|
77
linux-user/hppa/vdso.ld
Normal file
77
linux-user/hppa/vdso.ld
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for linux hppa vdso.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
VERSION {
|
||||||
|
/*
|
||||||
|
* The kernel's vdso32.lds.S attempts to export
|
||||||
|
* __kernel_sigtramp_rt32
|
||||||
|
* __kernel_restart_syscall32
|
||||||
|
* except that those symbols don't exist. The actual symbols are
|
||||||
|
* __kernel_sigtramp_rt
|
||||||
|
* __kernel_restart_syscall
|
||||||
|
* which means that nothing is exported at all.
|
||||||
|
* QEMU handles syscall restart internally, so we don't
|
||||||
|
* need to implement __kernel_restart_syscall at all.
|
||||||
|
*/
|
||||||
|
LINUX_5.18 {
|
||||||
|
local: *;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PHDRS {
|
||||||
|
phdr PT_PHDR FLAGS(4) PHDRS;
|
||||||
|
load PT_LOAD FLAGS(7) FILEHDR PHDRS;
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
|
note PT_NOTE FLAGS(4);
|
||||||
|
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
BIN
linux-user/hppa/vdso.so
Executable file
BIN
linux-user/hppa/vdso.so
Executable file
Binary file not shown.
11
linux-user/i386/Makefile.vdso
Normal file
11
linux-user/i386/Makefile.vdso
Normal 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 $<
|
@ -3,3 +3,10 @@ syscall_nr_generators += {
|
|||||||
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
||||||
output: '@BASENAME@_nr.h')
|
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)
|
||||||
|
@ -214,6 +214,17 @@ struct rt_sigframe {
|
|||||||
};
|
};
|
||||||
#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
|
#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
|
||||||
offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_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
|
#else
|
||||||
|
|
||||||
struct rt_sigframe {
|
struct rt_sigframe {
|
||||||
|
6
linux-user/i386/vdso-asmoffset.h
Normal file
6
linux-user/i386/vdso-asmoffset.h
Normal 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
143
linux-user/i386/vdso.S
Normal 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
76
linux-user/i386/vdso.ld
Normal 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
BIN
linux-user/i386/vdso.so
Executable file
Binary file not shown.
@ -3,7 +3,9 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu.h"
|
#include "qemu.h"
|
||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
|
#include "user-mmap.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#define NGROUPS 32
|
#define NGROUPS 32
|
||||||
|
|
||||||
@ -37,7 +39,7 @@ static int prepare_binprm(struct linux_binprm *bprm)
|
|||||||
int mode;
|
int mode;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (fstat(bprm->fd, &st) < 0) {
|
if (fstat(bprm->src.fd, &st) < 0) {
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ static int prepare_binprm(struct linux_binprm *bprm)
|
|||||||
bprm->e_gid = st.st_gid;
|
bprm->e_gid = st.st_gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE);
|
retval = read(bprm->src.fd, bprm->buf, BPRM_BUF_SIZE);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
perror("prepare_binprm");
|
perror("prepare_binprm");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
@ -76,6 +78,10 @@ static int prepare_binprm(struct linux_binprm *bprm)
|
|||||||
/* Make sure the rest of the loader won't read garbage. */
|
/* Make sure the rest of the loader won't read garbage. */
|
||||||
memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval);
|
memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bprm->src.cache = bprm->buf;
|
||||||
|
bprm->src.cache_size = retval;
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +144,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
|
|||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
bprm->fd = fdexec;
|
bprm->src.fd = fdexec;
|
||||||
bprm->filename = (char *)filename;
|
bprm->filename = (char *)filename;
|
||||||
bprm->argc = count(argv);
|
bprm->argc = count(argv);
|
||||||
bprm->argv = argv;
|
bprm->argv = argv;
|
||||||
@ -147,29 +153,112 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
|
|||||||
|
|
||||||
retval = prepare_binprm(bprm);
|
retval = prepare_binprm(bprm);
|
||||||
|
|
||||||
if (retval >= 0) {
|
if (retval < 4) {
|
||||||
if (bprm->buf[0] == 0x7f
|
return -ENOEXEC;
|
||||||
&& bprm->buf[1] == 'E'
|
|
||||||
&& bprm->buf[2] == 'L'
|
|
||||||
&& bprm->buf[3] == 'F') {
|
|
||||||
retval = load_elf_binary(bprm, infop);
|
|
||||||
#if defined(TARGET_HAS_BFLT)
|
|
||||||
} else if (bprm->buf[0] == 'b'
|
|
||||||
&& bprm->buf[1] == 'F'
|
|
||||||
&& bprm->buf[2] == 'L'
|
|
||||||
&& bprm->buf[3] == 'T') {
|
|
||||||
retval = load_flt_binary(bprm, infop);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
return -ENOEXEC;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (bprm->buf[0] == 0x7f
|
||||||
if (retval >= 0) {
|
&& bprm->buf[1] == 'E'
|
||||||
/* success. Initialize important registers */
|
&& bprm->buf[2] == 'L'
|
||||||
do_init_thread(regs, infop);
|
&& bprm->buf[3] == 'F') {
|
||||||
|
retval = load_elf_binary(bprm, infop);
|
||||||
|
#if defined(TARGET_HAS_BFLT)
|
||||||
|
} else if (bprm->buf[0] == 'b'
|
||||||
|
&& bprm->buf[1] == 'F'
|
||||||
|
&& bprm->buf[2] == 'L'
|
||||||
|
&& bprm->buf[3] == 'T') {
|
||||||
|
retval = load_flt_binary(bprm, infop);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
if (retval < 0) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
/* Success. Initialize important registers. */
|
||||||
|
do_init_thread(regs, infop);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool imgsrc_read(void *dst, off_t offset, size_t len,
|
||||||
|
const ImageSource *img, Error **errp)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (offset + len <= img->cache_size) {
|
||||||
|
memcpy(dst, img->cache + offset, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img->fd < 0) {
|
||||||
|
error_setg(errp, "read past end of buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pread(img->fd, dst, len, offset);
|
||||||
|
if (ret == len) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, errno, "Error reading file header");
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Incomplete read of file header");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *imgsrc_read_alloc(off_t offset, size_t len,
|
||||||
|
const ImageSource *img, Error **errp)
|
||||||
|
{
|
||||||
|
void *alloc = g_malloc(len);
|
||||||
|
bool ok = imgsrc_read(alloc, offset, len, img, errp);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
g_free(alloc);
|
||||||
|
alloc = NULL;
|
||||||
|
}
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot,
|
||||||
|
int flags, const ImageSource *src, abi_ulong offset)
|
||||||
|
{
|
||||||
|
const int prot_write = PROT_READ | PROT_WRITE;
|
||||||
|
abi_long ret;
|
||||||
|
void *haddr;
|
||||||
|
|
||||||
|
assert(flags == (MAP_PRIVATE | MAP_FIXED));
|
||||||
|
|
||||||
|
if (src->fd >= 0) {
|
||||||
|
return target_mmap(start, len, prot, flags, src->fd, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This case is for the vdso; we don't expect bad images.
|
||||||
|
* The mmap may extend beyond the end of the image, especially
|
||||||
|
* to the end of the page. Zero fill.
|
||||||
|
*/
|
||||||
|
assert(offset < src->cache_size);
|
||||||
|
|
||||||
|
ret = target_mmap(start, len, prot_write, flags | MAP_ANON, -1, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
haddr = lock_user(VERIFY_WRITE, start, len, 0);
|
||||||
|
assert(haddr != NULL);
|
||||||
|
if (offset + len <= src->cache_size) {
|
||||||
|
memcpy(haddr, src->cache + offset, len);
|
||||||
|
} else {
|
||||||
|
size_t rest = src->cache_size - offset;
|
||||||
|
memcpy(haddr, src->cache + offset, rest);
|
||||||
|
memset(haddr + rest, 0, len - rest);
|
||||||
|
}
|
||||||
|
unlock_user(haddr, start, len);
|
||||||
|
|
||||||
|
if (prot != prot_write) {
|
||||||
|
target_mprotect(start, len, prot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,48 @@
|
|||||||
#ifndef LINUX_USER_LOADER_H
|
#ifndef LINUX_USER_LOADER_H
|
||||||
#define LINUX_USER_LOADER_H
|
#define LINUX_USER_LOADER_H
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const void *cache;
|
||||||
|
unsigned int cache_size;
|
||||||
|
int fd;
|
||||||
|
} ImageSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* imgsrc_read: Read from ImageSource
|
||||||
|
* @dst: destination for read
|
||||||
|
* @offset: offset within file for read
|
||||||
|
* @len: size of the read
|
||||||
|
* @img: ImageSource to read from
|
||||||
|
* @errp: Error details.
|
||||||
|
*
|
||||||
|
* Read into @dst, using the cache when possible.
|
||||||
|
*/
|
||||||
|
bool imgsrc_read(void *dst, off_t offset, size_t len,
|
||||||
|
const ImageSource *img, Error **errp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* imgsrc_read_alloc: Read from ImageSource
|
||||||
|
* @offset: offset within file for read
|
||||||
|
* @size: size of the read
|
||||||
|
* @img: ImageSource to read from
|
||||||
|
* @errp: Error details.
|
||||||
|
*
|
||||||
|
* Read into newly allocated memory, using the cache when possible.
|
||||||
|
*/
|
||||||
|
void *imgsrc_read_alloc(off_t offset, size_t len,
|
||||||
|
const ImageSource *img, Error **errp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* imgsrc_mmap: Map from ImageSource
|
||||||
|
*
|
||||||
|
* If @src has a file descriptor, pass on to target_mmap. Otherwise,
|
||||||
|
* this is "mapping" from a host buffer, which resolves to memcpy.
|
||||||
|
* Therefore, flags must be MAP_PRIVATE | MAP_FIXED; the argument is
|
||||||
|
* retained for clarity.
|
||||||
|
*/
|
||||||
|
abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot,
|
||||||
|
int flags, const ImageSource *src, abi_ulong offset);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read a good amount of data initially, to hopefully get all the
|
* Read a good amount of data initially, to hopefully get all the
|
||||||
* program headers loaded.
|
* program headers loaded.
|
||||||
@ -29,15 +71,15 @@
|
|||||||
* used when loading binaries.
|
* used when loading binaries.
|
||||||
*/
|
*/
|
||||||
struct linux_binprm {
|
struct linux_binprm {
|
||||||
char buf[BPRM_BUF_SIZE] __attribute__((aligned));
|
char buf[BPRM_BUF_SIZE] __attribute__((aligned));
|
||||||
abi_ulong p;
|
ImageSource src;
|
||||||
int fd;
|
abi_ulong p;
|
||||||
int e_uid, e_gid;
|
int e_uid, e_gid;
|
||||||
int argc, envc;
|
int argc, envc;
|
||||||
char **argv;
|
char **argv;
|
||||||
char **envp;
|
char **envp;
|
||||||
char *filename; /* Name of binary */
|
char *filename; /* Name of binary */
|
||||||
int (*core_dump)(int, const CPUArchState *); /* coredump routine */
|
int (*core_dump)(int, const CPUArchState *); /* coredump routine */
|
||||||
};
|
};
|
||||||
|
|
||||||
void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);
|
void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);
|
||||||
|
11
linux-user/loongarch64/Makefile.vdso
Normal file
11
linux-user/loongarch64/Makefile.vdso
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
include $(BUILD_DIR)/tests/tcg/loongarch64-linux-user/config-target.mak
|
||||||
|
|
||||||
|
SUBDIR = $(SRC_PATH)/linux-user/loongarch64
|
||||||
|
VPATH += $(SUBDIR)
|
||||||
|
|
||||||
|
all: $(SUBDIR)/vdso.so
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h
|
||||||
|
$(CC) -o $@ -nostdlib -shared -fpic -Wl,-h,linux-vdso.so.1 \
|
||||||
|
-Wl,--build-id=sha1 -Wl,--hash-style=both \
|
||||||
|
-Wl,--no-warn-rwx-segments -Wl,-T,$(SUBDIR)/vdso.ld $<
|
4
linux-user/loongarch64/meson.build
Normal file
4
linux-user/loongarch64/meson.build
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
vdso_inc = gen_vdso.process('vdso.so',
|
||||||
|
extra_args: ['-r', '__vdso_rt_sigreturn'])
|
||||||
|
|
||||||
|
linux_user_ss.add(when: 'TARGET_LOONGARCH64', if_true: vdso_inc)
|
@ -10,9 +10,9 @@
|
|||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "linux-user/trace.h"
|
#include "linux-user/trace.h"
|
||||||
|
|
||||||
#include "target/loongarch/internals.h"
|
#include "target/loongarch/internals.h"
|
||||||
#include "target/loongarch/vec.h"
|
#include "target/loongarch/vec.h"
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
/* FP context was used */
|
/* FP context was used */
|
||||||
#define SC_USED_FP (1 << 0)
|
#define SC_USED_FP (1 << 0)
|
||||||
@ -24,6 +24,11 @@ struct target_sigcontext {
|
|||||||
uint64_t sc_extcontext[0] QEMU_ALIGNED(16);
|
uint64_t sc_extcontext[0] QEMU_ALIGNED(16);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_pc)
|
||||||
|
!= offsetof_sigcontext_pc);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_regs)
|
||||||
|
!= offsetof_sigcontext_gr);
|
||||||
|
|
||||||
#define FPU_CTX_MAGIC 0x46505501
|
#define FPU_CTX_MAGIC 0x46505501
|
||||||
#define FPU_CTX_ALIGN 8
|
#define FPU_CTX_ALIGN 8
|
||||||
@ -33,6 +38,9 @@ struct target_fpu_context {
|
|||||||
uint32_t fcsr;
|
uint32_t fcsr;
|
||||||
} QEMU_ALIGNED(FPU_CTX_ALIGN);
|
} QEMU_ALIGNED(FPU_CTX_ALIGN);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs)
|
||||||
|
!= offsetof_fpucontext_fr);
|
||||||
|
|
||||||
#define CONTEXT_INFO_ALIGN 16
|
#define CONTEXT_INFO_ALIGN 16
|
||||||
struct target_sctx_info {
|
struct target_sctx_info {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@ -40,6 +48,8 @@ struct target_sctx_info {
|
|||||||
uint64_t padding;
|
uint64_t padding;
|
||||||
} QEMU_ALIGNED(CONTEXT_INFO_ALIGN);
|
} QEMU_ALIGNED(CONTEXT_INFO_ALIGN);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info);
|
||||||
|
|
||||||
struct target_ucontext {
|
struct target_ucontext {
|
||||||
abi_ulong tuc_flags;
|
abi_ulong tuc_flags;
|
||||||
abi_ptr tuc_link;
|
abi_ptr tuc_link;
|
||||||
@ -54,6 +64,11 @@ struct target_rt_sigframe {
|
|||||||
struct target_ucontext rs_uc;
|
struct target_ucontext rs_uc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe)
|
||||||
|
!= sizeof_rt_sigframe);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, rs_uc.tuc_mcontext)
|
||||||
|
!= offsetof_sigcontext);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These two structures are not present in guest memory, are private
|
* These two structures are not present in guest memory, are private
|
||||||
* to the signal implementation, but are largely copied from the
|
* to the signal implementation, but are largely copied from the
|
||||||
|
8
linux-user/loongarch64/vdso-asmoffset.h
Normal file
8
linux-user/loongarch64/vdso-asmoffset.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#define sizeof_rt_sigframe 0x240
|
||||||
|
#define sizeof_sigcontext 0x110
|
||||||
|
#define sizeof_sctx_info 0x10
|
||||||
|
|
||||||
|
#define offsetof_sigcontext 0x130
|
||||||
|
#define offsetof_sigcontext_pc 0
|
||||||
|
#define offsetof_sigcontext_gr 8
|
||||||
|
#define offsetof_fpucontext_fr 0
|
130
linux-user/loongarch64/vdso.S
Normal file
130
linux-user/loongarch64/vdso.S
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Loongarch64 linux replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
#include <asm/errno.h>
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
.macro endf name
|
||||||
|
.globl \name
|
||||||
|
.type \name, @function
|
||||||
|
.size \name, . - \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro vdso_syscall name, nr
|
||||||
|
\name:
|
||||||
|
li.w $a7, \nr
|
||||||
|
syscall 0
|
||||||
|
jr $ra
|
||||||
|
endf \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.cfi_startproc
|
||||||
|
|
||||||
|
vdso_syscall __vdso_gettimeofday, __NR_gettimeofday
|
||||||
|
vdso_syscall __vdso_clock_gettime, __NR_clock_gettime
|
||||||
|
vdso_syscall __vdso_clock_getres, __NR_clock_getres
|
||||||
|
vdso_syscall __vdso_getcpu, __NR_getcpu
|
||||||
|
|
||||||
|
.cfi_endproc
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#define B_GR offsetof_sigcontext_gr
|
||||||
|
#define B_FR sizeof_sigcontext + sizeof_sctx_info + offsetof_fpucontext_fr
|
||||||
|
|
||||||
|
.cfi_def_cfa 2, offsetof_sigcontext
|
||||||
|
|
||||||
|
/* Return address */
|
||||||
|
.cfi_return_column 64
|
||||||
|
.cfi_offset 64, offsetof_sigcontext_pc /* pc */
|
||||||
|
|
||||||
|
/* Integer registers */
|
||||||
|
.cfi_offset 1, B_GR + 1 * 8
|
||||||
|
.cfi_offset 2, B_GR + 2 * 8
|
||||||
|
.cfi_offset 3, B_GR + 3 * 8
|
||||||
|
.cfi_offset 4, B_GR + 4 * 8
|
||||||
|
.cfi_offset 5, B_GR + 5 * 8
|
||||||
|
.cfi_offset 6, B_GR + 6 * 8
|
||||||
|
.cfi_offset 7, B_GR + 7 * 8
|
||||||
|
.cfi_offset 8, B_GR + 8 * 8
|
||||||
|
.cfi_offset 9, B_GR + 9 * 8
|
||||||
|
.cfi_offset 10, B_GR + 10 * 8
|
||||||
|
.cfi_offset 11, B_GR + 11 * 8
|
||||||
|
.cfi_offset 12, B_GR + 12 * 8
|
||||||
|
.cfi_offset 13, B_GR + 13 * 8
|
||||||
|
.cfi_offset 14, B_GR + 14 * 8
|
||||||
|
.cfi_offset 15, B_GR + 15 * 8
|
||||||
|
.cfi_offset 16, B_GR + 16 * 8
|
||||||
|
.cfi_offset 17, B_GR + 17 * 8
|
||||||
|
.cfi_offset 18, B_GR + 18 * 8
|
||||||
|
.cfi_offset 19, B_GR + 19 * 8
|
||||||
|
.cfi_offset 20, B_GR + 20 * 8
|
||||||
|
.cfi_offset 21, B_GR + 21 * 8
|
||||||
|
.cfi_offset 22, B_GR + 22 * 8
|
||||||
|
.cfi_offset 23, B_GR + 23 * 8
|
||||||
|
.cfi_offset 24, B_GR + 24 * 8
|
||||||
|
.cfi_offset 25, B_GR + 25 * 8
|
||||||
|
.cfi_offset 26, B_GR + 26 * 8
|
||||||
|
.cfi_offset 27, B_GR + 27 * 8
|
||||||
|
.cfi_offset 28, B_GR + 28 * 8
|
||||||
|
.cfi_offset 29, B_GR + 29 * 8
|
||||||
|
.cfi_offset 30, B_GR + 30 * 8
|
||||||
|
.cfi_offset 31, B_GR + 31 * 8
|
||||||
|
|
||||||
|
/* Floating point registers */
|
||||||
|
.cfi_offset 32, B_FR + 0
|
||||||
|
.cfi_offset 33, B_FR + 1 * 8
|
||||||
|
.cfi_offset 34, B_FR + 2 * 8
|
||||||
|
.cfi_offset 35, B_FR + 3 * 8
|
||||||
|
.cfi_offset 36, B_FR + 4 * 8
|
||||||
|
.cfi_offset 37, B_FR + 5 * 8
|
||||||
|
.cfi_offset 38, B_FR + 6 * 8
|
||||||
|
.cfi_offset 39, B_FR + 7 * 8
|
||||||
|
.cfi_offset 40, B_FR + 8 * 8
|
||||||
|
.cfi_offset 41, B_FR + 9 * 8
|
||||||
|
.cfi_offset 42, B_FR + 10 * 8
|
||||||
|
.cfi_offset 43, B_FR + 11 * 8
|
||||||
|
.cfi_offset 44, B_FR + 12 * 8
|
||||||
|
.cfi_offset 45, B_FR + 13 * 8
|
||||||
|
.cfi_offset 46, B_FR + 14 * 8
|
||||||
|
.cfi_offset 47, B_FR + 15 * 8
|
||||||
|
.cfi_offset 48, B_FR + 16 * 8
|
||||||
|
.cfi_offset 49, B_FR + 17 * 8
|
||||||
|
.cfi_offset 50, B_FR + 18 * 8
|
||||||
|
.cfi_offset 51, B_FR + 19 * 8
|
||||||
|
.cfi_offset 52, B_FR + 20 * 8
|
||||||
|
.cfi_offset 53, B_FR + 21 * 8
|
||||||
|
.cfi_offset 54, B_FR + 22 * 8
|
||||||
|
.cfi_offset 55, B_FR + 23 * 8
|
||||||
|
.cfi_offset 56, B_FR + 24 * 8
|
||||||
|
.cfi_offset 57, B_FR + 25 * 8
|
||||||
|
.cfi_offset 58, B_FR + 26 * 8
|
||||||
|
.cfi_offset 59, B_FR + 27 * 8
|
||||||
|
.cfi_offset 60, B_FR + 28 * 8
|
||||||
|
.cfi_offset 61, B_FR + 29 * 8
|
||||||
|
.cfi_offset 62, B_FR + 30 * 8
|
||||||
|
.cfi_offset 63, B_FR + 31 * 8
|
||||||
|
|
||||||
|
nop
|
||||||
|
|
||||||
|
__vdso_rt_sigreturn:
|
||||||
|
li.w $a7, __NR_rt_sigreturn
|
||||||
|
syscall 0
|
||||||
|
.cfi_endproc
|
||||||
|
endf __vdso_rt_sigreturn
|
73
linux-user/loongarch64/vdso.ld
Normal file
73
linux-user/loongarch64/vdso.ld
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for linux loongarch64 replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
VERSION {
|
||||||
|
LINUX_5.10 {
|
||||||
|
global:
|
||||||
|
__vdso_getcpu;
|
||||||
|
__vdso_clock_getres;
|
||||||
|
__vdso_clock_gettime;
|
||||||
|
__vdso_gettimeofday;
|
||||||
|
__vdso_rt_sigreturn;
|
||||||
|
|
||||||
|
local: *;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PHDRS {
|
||||||
|
phdr PT_PHDR FLAGS(4) PHDRS;
|
||||||
|
load PT_LOAD FLAGS(7) FILEHDR PHDRS;
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
|
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||||
|
note PT_NOTE FLAGS(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/*
|
||||||
|
* We can't prelink to any address without knowing something about
|
||||||
|
* the virtual memory space of the host, since that leaks over into
|
||||||
|
* the available memory space of the guest.
|
||||||
|
*/
|
||||||
|
. = 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 =0xd503201f
|
||||||
|
}
|
BIN
linux-user/loongarch64/vdso.so
Executable file
BIN
linux-user/loongarch64/vdso.so
Executable file
Binary file not shown.
@ -28,18 +28,25 @@ linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c'))
|
|||||||
linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c'))
|
linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c'))
|
||||||
linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c'))
|
linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c'))
|
||||||
|
|
||||||
|
|
||||||
syscall_nr_generators = {}
|
syscall_nr_generators = {}
|
||||||
|
|
||||||
|
gen_vdso_exe = executable('gen-vdso', 'gen-vdso.c',
|
||||||
|
native: true, build_by_default: false)
|
||||||
|
gen_vdso = generator(gen_vdso_exe, output: '@BASENAME@.c.inc',
|
||||||
|
arguments: ['-o', '@OUTPUT@', '@EXTRA_ARGS@', '@INPUT@'])
|
||||||
|
|
||||||
|
subdir('aarch64')
|
||||||
subdir('alpha')
|
subdir('alpha')
|
||||||
subdir('arm')
|
subdir('arm')
|
||||||
subdir('hppa')
|
subdir('hppa')
|
||||||
subdir('i386')
|
subdir('i386')
|
||||||
|
subdir('loongarch64')
|
||||||
subdir('m68k')
|
subdir('m68k')
|
||||||
subdir('microblaze')
|
subdir('microblaze')
|
||||||
subdir('mips64')
|
subdir('mips64')
|
||||||
subdir('mips')
|
subdir('mips')
|
||||||
subdir('ppc')
|
subdir('ppc')
|
||||||
|
subdir('riscv')
|
||||||
subdir('s390x')
|
subdir('s390x')
|
||||||
subdir('sh4')
|
subdir('sh4')
|
||||||
subdir('sparc')
|
subdir('sparc')
|
||||||
|
20
linux-user/ppc/Makefile.vdso
Normal file
20
linux-user/ppc/Makefile.vdso
Normal file
@ -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 $<
|
@ -3,3 +3,15 @@ syscall_nr_generators += {
|
|||||||
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
||||||
output: '@BASENAME@_nr.h')
|
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])
|
||||||
|
@ -21,14 +21,7 @@
|
|||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "linux-user/trace.h"
|
#include "linux-user/trace.h"
|
||||||
|
#include "vdso-asmoffset.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
|
|
||||||
|
|
||||||
/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC;
|
/* 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. */
|
on 64-bit PPC, sigcontext and mcontext are one and the same. */
|
||||||
@ -73,6 +66,16 @@ struct target_mcontext {
|
|||||||
#endif
|
#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. */
|
/* See arch/powerpc/include/asm/sigcontext.h. */
|
||||||
struct target_sigcontext {
|
struct target_sigcontext {
|
||||||
target_ulong _unused[4];
|
target_ulong _unused[4];
|
||||||
@ -161,6 +164,7 @@ struct target_ucontext {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !defined(TARGET_PPC64)
|
||||||
/* See arch/powerpc/kernel/signal_32.c. */
|
/* See arch/powerpc/kernel/signal_32.c. */
|
||||||
struct target_sigframe {
|
struct target_sigframe {
|
||||||
struct target_sigcontext sctx;
|
struct target_sigcontext sctx;
|
||||||
@ -168,6 +172,10 @@ struct target_sigframe {
|
|||||||
int32_t abigap[56];
|
int32_t abigap[56];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigframe, mctx)
|
||||||
|
!= offsetof_sigframe_mcontext);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
|
|
||||||
#define TARGET_TRAMP_SIZE 6
|
#define TARGET_TRAMP_SIZE 6
|
||||||
@ -184,6 +192,10 @@ struct target_rt_sigframe {
|
|||||||
char abigap[288];
|
char abigap[288];
|
||||||
} __attribute__((aligned(16)));
|
} __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe,
|
||||||
|
uc.tuc_sigcontext.mcontext)
|
||||||
|
!= offsetof_rt_sigframe_mcontext);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
struct target_rt_sigframe {
|
struct target_rt_sigframe {
|
||||||
@ -192,6 +204,9 @@ struct target_rt_sigframe {
|
|||||||
int32_t abigap[56];
|
int32_t abigap[56];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext)
|
||||||
|
!= offsetof_rt_sigframe_mcontext);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
|
70
linux-user/ppc/vdso-32.ld
Normal file
70
linux-user/ppc/vdso-32.ld
Normal file
@ -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
|
||||||
|
}
|
BIN
linux-user/ppc/vdso-32.so
Executable file
BIN
linux-user/ppc/vdso-32.so
Executable file
Binary file not shown.
68
linux-user/ppc/vdso-64.ld
Normal file
68
linux-user/ppc/vdso-64.ld
Normal file
@ -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
|
||||||
|
}
|
BIN
linux-user/ppc/vdso-64.so
Executable file
BIN
linux-user/ppc/vdso-64.so
Executable file
Binary file not shown.
BIN
linux-user/ppc/vdso-64le.so
Executable file
BIN
linux-user/ppc/vdso-64le.so
Executable file
Binary file not shown.
20
linux-user/ppc/vdso-asmoffset.h
Normal file
20
linux-user/ppc/vdso-asmoffset.h
Normal file
@ -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
|
239
linux-user/ppc/vdso.S
Normal file
239
linux-user/ppc/vdso.S
Normal file
@ -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
|
@ -32,6 +32,7 @@ struct image_info {
|
|||||||
abi_ulong brk;
|
abi_ulong brk;
|
||||||
abi_ulong start_stack;
|
abi_ulong start_stack;
|
||||||
abi_ulong stack_limit;
|
abi_ulong stack_limit;
|
||||||
|
abi_ulong vdso;
|
||||||
abi_ulong entry;
|
abi_ulong entry;
|
||||||
abi_ulong code_offset;
|
abi_ulong code_offset;
|
||||||
abi_ulong data_offset;
|
abi_ulong data_offset;
|
||||||
|
15
linux-user/riscv/Makefile.vdso
Normal file
15
linux-user/riscv/Makefile.vdso
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
include $(BUILD_DIR)/tests/tcg/riscv64-linux-user/config-target.mak
|
||||||
|
|
||||||
|
SUBDIR = $(SRC_PATH)/linux-user/riscv
|
||||||
|
VPATH += $(SUBDIR)
|
||||||
|
|
||||||
|
all: $(SUBDIR)/vdso-32.so $(SUBDIR)/vdso-64.so
|
||||||
|
|
||||||
|
LDFLAGS = -nostdlib -shared -fpic -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \
|
||||||
|
-Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso-32.so: vdso.S vdso.ld vdso-asmoffset.h
|
||||||
|
$(CC) -o $@ $(LDFLAGS) -mabi=ilp32d -march=rv32g $<
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso-64.so: vdso.S vdso.ld vdso-asmoffset.h
|
||||||
|
$(CC) -o $@ $(LDFLAGS) -mabi=lp64d -march=rv64g $<
|
7
linux-user/riscv/meson.build
Normal file
7
linux-user/riscv/meson.build
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
vdso_32_inc = gen_vdso.process('vdso-32.so',
|
||||||
|
extra_args: ['-r', '__vdso_rt_sigreturn'])
|
||||||
|
vdso_64_inc = gen_vdso.process('vdso-64.so',
|
||||||
|
extra_args: ['-r', '__vdso_rt_sigreturn'])
|
||||||
|
|
||||||
|
linux_user_ss.add(when: 'TARGET_RISCV32', if_true: vdso_32_inc)
|
||||||
|
linux_user_ss.add(when: 'TARGET_RISCV64', if_true: vdso_64_inc)
|
@ -21,6 +21,7 @@
|
|||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "linux-user/trace.h"
|
#include "linux-user/trace.h"
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
/* Signal handler invocation must be transparent for the code being
|
/* Signal handler invocation must be transparent for the code being
|
||||||
interrupted. Complete CPU (hart) state is saved on entry and restored
|
interrupted. Complete CPU (hart) state is saved on entry and restored
|
||||||
@ -37,6 +38,8 @@ struct target_sigcontext {
|
|||||||
uint32_t fcsr;
|
uint32_t fcsr;
|
||||||
}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */
|
}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, fpr) != offsetof_freg0);
|
||||||
|
|
||||||
struct target_ucontext {
|
struct target_ucontext {
|
||||||
abi_ulong uc_flags;
|
abi_ulong uc_flags;
|
||||||
abi_ptr uc_link;
|
abi_ptr uc_link;
|
||||||
@ -51,6 +54,11 @@ struct target_rt_sigframe {
|
|||||||
struct target_ucontext uc;
|
struct target_ucontext uc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe)
|
||||||
|
!= sizeof_rt_sigframe);
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.uc_mcontext)
|
||||||
|
!= offsetof_uc_mcontext);
|
||||||
|
|
||||||
static abi_ulong get_sigframe(struct target_sigaction *ka,
|
static abi_ulong get_sigframe(struct target_sigaction *ka,
|
||||||
CPURISCVState *regs, size_t framesize)
|
CPURISCVState *regs, size_t framesize)
|
||||||
{
|
{
|
||||||
|
BIN
linux-user/riscv/vdso-32.so
Executable file
BIN
linux-user/riscv/vdso-32.so
Executable file
Binary file not shown.
BIN
linux-user/riscv/vdso-64.so
Executable file
BIN
linux-user/riscv/vdso-64.so
Executable file
Binary file not shown.
9
linux-user/riscv/vdso-asmoffset.h
Normal file
9
linux-user/riscv/vdso-asmoffset.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifdef TARGET_ABI32
|
||||||
|
# define sizeof_rt_sigframe 0x2b0
|
||||||
|
# define offsetof_uc_mcontext 0x120
|
||||||
|
# define offsetof_freg0 0x80
|
||||||
|
#else
|
||||||
|
# define sizeof_rt_sigframe 0x340
|
||||||
|
# define offsetof_uc_mcontext 0x130
|
||||||
|
# define offsetof_freg0 0x100
|
||||||
|
#endif
|
187
linux-user/riscv/vdso.S
Normal file
187
linux-user/riscv/vdso.S
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* RISC-V linux replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
#include <asm/errno.h>
|
||||||
|
|
||||||
|
#if __riscv_xlen == 32
|
||||||
|
# define TARGET_ABI32
|
||||||
|
#endif
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
.macro endf name
|
||||||
|
.globl \name
|
||||||
|
.type \name, @function
|
||||||
|
.size \name, . - \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro raw_syscall nr
|
||||||
|
li a7, \nr
|
||||||
|
ecall
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro vdso_syscall name, nr
|
||||||
|
\name:
|
||||||
|
raw_syscall \nr
|
||||||
|
ret
|
||||||
|
endf \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
__vdso_gettimeofday:
|
||||||
|
.cfi_startproc
|
||||||
|
#ifdef __NR_gettimeofday
|
||||||
|
raw_syscall __NR_gettimeofday
|
||||||
|
ret
|
||||||
|
#else
|
||||||
|
/* No gettimeofday, fall back to clock_gettime64. */
|
||||||
|
beq a1, zero, 1f
|
||||||
|
sw zero, 0(a1) /* tz->tz_minuteswest = 0 */
|
||||||
|
sw zero, 4(a1) /* tz->tz_dsttime = 0 */
|
||||||
|
1: addi sp, sp, -32
|
||||||
|
.cfi_adjust_cfa_offset 32
|
||||||
|
sw a0, 16(sp) /* save tv */
|
||||||
|
mv a0, sp
|
||||||
|
raw_syscall __NR_clock_gettime64
|
||||||
|
lw t0, 0(sp) /* timespec.tv_sec.low */
|
||||||
|
lw t1, 4(sp) /* timespec.tv_sec.high */
|
||||||
|
lw t2, 8(sp) /* timespec.tv_nsec.low */
|
||||||
|
lw a1, 16(sp) /* restore tv */
|
||||||
|
addi sp, sp, 32
|
||||||
|
.cfi_adjust_cfa_offset -32
|
||||||
|
bne a0, zero, 9f /* syscall error? */
|
||||||
|
li a0, -EOVERFLOW
|
||||||
|
bne t1, zero, 9f /* y2038? */
|
||||||
|
li a0, 0
|
||||||
|
li t3, 1000
|
||||||
|
divu t2, t2, t3 /* nsec -> usec */
|
||||||
|
sw t0, 0(a1) /* tz->tv_sec */
|
||||||
|
sw t2, 4(a1) /* tz->tv_usec */
|
||||||
|
9: ret
|
||||||
|
#endif
|
||||||
|
.cfi_endproc
|
||||||
|
endf __vdso_gettimeofday
|
||||||
|
|
||||||
|
.cfi_startproc
|
||||||
|
|
||||||
|
#ifdef __NR_clock_gettime
|
||||||
|
vdso_syscall __vdso_clock_gettime, __NR_clock_gettime
|
||||||
|
#else
|
||||||
|
vdso_syscall __vdso_clock_gettime, __NR_clock_gettime64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __NR_clock_getres
|
||||||
|
vdso_syscall __vdso_clock_getres, __NR_clock_getres
|
||||||
|
#else
|
||||||
|
vdso_syscall __vdso_clock_getres, __NR_clock_getres_time64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vdso_syscall __vdso_getcpu, __NR_getcpu
|
||||||
|
|
||||||
|
__vdso_flush_icache:
|
||||||
|
/* qemu does not need to flush the icache */
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
endf __vdso_flush_icache
|
||||||
|
|
||||||
|
.cfi_endproc
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#define sizeof_reg (__riscv_xlen / 4)
|
||||||
|
#define sizeof_freg 8
|
||||||
|
#define B_GR (offsetof_uc_mcontext - sizeof_rt_sigframe)
|
||||||
|
#define B_FR (offsetof_uc_mcontext - sizeof_rt_sigframe + offsetof_freg0)
|
||||||
|
|
||||||
|
.cfi_def_cfa 2, sizeof_rt_sigframe
|
||||||
|
|
||||||
|
/* Return address */
|
||||||
|
.cfi_return_column 64
|
||||||
|
.cfi_offset 64, B_GR + 0 /* pc */
|
||||||
|
|
||||||
|
/* Integer registers */
|
||||||
|
.cfi_offset 1, B_GR + 1 * sizeof_reg /* r1 (ra) */
|
||||||
|
.cfi_offset 2, B_GR + 2 * sizeof_reg /* r2 (sp) */
|
||||||
|
.cfi_offset 3, B_GR + 3 * sizeof_reg
|
||||||
|
.cfi_offset 4, B_GR + 4 * sizeof_reg
|
||||||
|
.cfi_offset 5, B_GR + 5 * sizeof_reg
|
||||||
|
.cfi_offset 6, B_GR + 6 * sizeof_reg
|
||||||
|
.cfi_offset 7, B_GR + 7 * sizeof_reg
|
||||||
|
.cfi_offset 8, B_GR + 8 * sizeof_reg
|
||||||
|
.cfi_offset 9, B_GR + 9 * sizeof_reg
|
||||||
|
.cfi_offset 10, B_GR + 10 * sizeof_reg
|
||||||
|
.cfi_offset 11, B_GR + 11 * sizeof_reg
|
||||||
|
.cfi_offset 12, B_GR + 12 * sizeof_reg
|
||||||
|
.cfi_offset 13, B_GR + 13 * sizeof_reg
|
||||||
|
.cfi_offset 14, B_GR + 14 * sizeof_reg
|
||||||
|
.cfi_offset 15, B_GR + 15 * sizeof_reg
|
||||||
|
.cfi_offset 16, B_GR + 16 * sizeof_reg
|
||||||
|
.cfi_offset 17, B_GR + 17 * sizeof_reg
|
||||||
|
.cfi_offset 18, B_GR + 18 * sizeof_reg
|
||||||
|
.cfi_offset 19, B_GR + 19 * sizeof_reg
|
||||||
|
.cfi_offset 20, B_GR + 20 * sizeof_reg
|
||||||
|
.cfi_offset 21, B_GR + 21 * sizeof_reg
|
||||||
|
.cfi_offset 22, B_GR + 22 * sizeof_reg
|
||||||
|
.cfi_offset 23, B_GR + 23 * sizeof_reg
|
||||||
|
.cfi_offset 24, B_GR + 24 * sizeof_reg
|
||||||
|
.cfi_offset 25, B_GR + 25 * sizeof_reg
|
||||||
|
.cfi_offset 26, B_GR + 26 * sizeof_reg
|
||||||
|
.cfi_offset 27, B_GR + 27 * sizeof_reg
|
||||||
|
.cfi_offset 28, B_GR + 28 * sizeof_reg
|
||||||
|
.cfi_offset 29, B_GR + 29 * sizeof_reg
|
||||||
|
.cfi_offset 30, B_GR + 30 * sizeof_reg
|
||||||
|
.cfi_offset 31, B_GR + 31 * sizeof_reg /* r31 */
|
||||||
|
|
||||||
|
.cfi_offset 32, B_FR + 0 /* f0 */
|
||||||
|
.cfi_offset 33, B_FR + 1 * sizeof_freg /* f1 */
|
||||||
|
.cfi_offset 34, B_FR + 2 * sizeof_freg
|
||||||
|
.cfi_offset 35, B_FR + 3 * sizeof_freg
|
||||||
|
.cfi_offset 36, B_FR + 4 * sizeof_freg
|
||||||
|
.cfi_offset 37, B_FR + 5 * sizeof_freg
|
||||||
|
.cfi_offset 38, B_FR + 6 * sizeof_freg
|
||||||
|
.cfi_offset 39, B_FR + 7 * sizeof_freg
|
||||||
|
.cfi_offset 40, B_FR + 8 * sizeof_freg
|
||||||
|
.cfi_offset 41, B_FR + 9 * sizeof_freg
|
||||||
|
.cfi_offset 42, B_FR + 10 * sizeof_freg
|
||||||
|
.cfi_offset 43, B_FR + 11 * sizeof_freg
|
||||||
|
.cfi_offset 44, B_FR + 12 * sizeof_freg
|
||||||
|
.cfi_offset 45, B_FR + 13 * sizeof_freg
|
||||||
|
.cfi_offset 46, B_FR + 14 * sizeof_freg
|
||||||
|
.cfi_offset 47, B_FR + 15 * sizeof_freg
|
||||||
|
.cfi_offset 48, B_FR + 16 * sizeof_freg
|
||||||
|
.cfi_offset 49, B_FR + 17 * sizeof_freg
|
||||||
|
.cfi_offset 50, B_FR + 18 * sizeof_freg
|
||||||
|
.cfi_offset 51, B_FR + 19 * sizeof_freg
|
||||||
|
.cfi_offset 52, B_FR + 20 * sizeof_freg
|
||||||
|
.cfi_offset 53, B_FR + 21 * sizeof_freg
|
||||||
|
.cfi_offset 54, B_FR + 22 * sizeof_freg
|
||||||
|
.cfi_offset 55, B_FR + 23 * sizeof_freg
|
||||||
|
.cfi_offset 56, B_FR + 24 * sizeof_freg
|
||||||
|
.cfi_offset 57, B_FR + 25 * sizeof_freg
|
||||||
|
.cfi_offset 58, B_FR + 26 * sizeof_freg
|
||||||
|
.cfi_offset 59, B_FR + 27 * sizeof_freg
|
||||||
|
.cfi_offset 60, B_FR + 28 * sizeof_freg
|
||||||
|
.cfi_offset 61, B_FR + 29 * sizeof_freg
|
||||||
|
.cfi_offset 62, B_FR + 30 * sizeof_freg
|
||||||
|
.cfi_offset 63, B_FR + 31 * sizeof_freg /* f31 */
|
||||||
|
|
||||||
|
nop
|
||||||
|
|
||||||
|
__vdso_rt_sigreturn:
|
||||||
|
raw_syscall __NR_rt_sigreturn
|
||||||
|
endf __vdso_rt_sigreturn
|
||||||
|
|
||||||
|
.cfi_endproc
|
74
linux-user/riscv/vdso.ld
Normal file
74
linux-user/riscv/vdso.ld
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for linux riscv replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
VERSION {
|
||||||
|
LINUX_4.15 {
|
||||||
|
global:
|
||||||
|
__vdso_rt_sigreturn;
|
||||||
|
__vdso_gettimeofday;
|
||||||
|
__vdso_clock_gettime;
|
||||||
|
__vdso_clock_getres;
|
||||||
|
__vdso_getcpu;
|
||||||
|
__vdso_flush_icache;
|
||||||
|
|
||||||
|
local: *;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PHDRS {
|
||||||
|
phdr PT_PHDR FLAGS(4) PHDRS;
|
||||||
|
load PT_LOAD FLAGS(7) FILEHDR PHDRS;
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
|
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||||
|
note PT_NOTE FLAGS(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/*
|
||||||
|
* We can't prelink to any address without knowing something about
|
||||||
|
* the virtual memory space of the host, since that leaks over into
|
||||||
|
* the available memory space of the guest.
|
||||||
|
*/
|
||||||
|
. = 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 =0xd503201f
|
||||||
|
}
|
11
linux-user/s390x/Makefile.vdso
Normal file
11
linux-user/s390x/Makefile.vdso
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
include $(BUILD_DIR)/tests/tcg/s390x-linux-user/config-target.mak
|
||||||
|
|
||||||
|
SUBDIR = $(SRC_PATH)/linux-user/s390x
|
||||||
|
VPATH += $(SUBDIR)
|
||||||
|
|
||||||
|
all: $(SUBDIR)/vdso.so
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h
|
||||||
|
$(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso64.so.1 \
|
||||||
|
-Wl,--build-id=sha1 -Wl,--hash-style=both \
|
||||||
|
-Wl,-T,$(SUBDIR)/vdso.ld $<
|
@ -3,3 +3,9 @@ syscall_nr_generators += {
|
|||||||
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
||||||
output: '@BASENAME@_nr.h')
|
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_S390X', if_true: vdso_inc)
|
||||||
|
@ -21,13 +21,12 @@
|
|||||||
#include "user-internals.h"
|
#include "user-internals.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "linux-user/trace.h"
|
#include "linux-user/trace.h"
|
||||||
|
#include "vdso-asmoffset.h"
|
||||||
|
|
||||||
#define __NUM_GPRS 16
|
#define __NUM_GPRS 16
|
||||||
#define __NUM_FPRS 16
|
#define __NUM_FPRS 16
|
||||||
#define __NUM_ACRS 16
|
#define __NUM_ACRS 16
|
||||||
|
|
||||||
#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */
|
|
||||||
|
|
||||||
#define _SIGCONTEXT_NSIG 64
|
#define _SIGCONTEXT_NSIG 64
|
||||||
#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */
|
#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */
|
||||||
#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW)
|
#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW)
|
||||||
@ -63,7 +62,7 @@ typedef struct {
|
|||||||
} target_sigcontext;
|
} target_sigcontext;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t callee_used_stack[__SIGNAL_FRAMESIZE];
|
uint8_t callee_used_stack[STACK_FRAME_OVERHEAD];
|
||||||
target_sigcontext sc;
|
target_sigcontext sc;
|
||||||
target_sigregs sregs;
|
target_sigregs sregs;
|
||||||
int signo;
|
int signo;
|
||||||
@ -83,7 +82,7 @@ struct target_ucontext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t callee_used_stack[__SIGNAL_FRAMESIZE];
|
uint8_t callee_used_stack[STACK_FRAME_OVERHEAD];
|
||||||
/*
|
/*
|
||||||
* This field is no longer initialized by the kernel, but it's still a part
|
* This field is no longer initialized by the kernel, but it's still a part
|
||||||
* of the ABI.
|
* of the ABI.
|
||||||
|
2
linux-user/s390x/vdso-asmoffset.h
Normal file
2
linux-user/s390x/vdso-asmoffset.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/* Minimum stack frame size */
|
||||||
|
#define STACK_FRAME_OVERHEAD 160
|
61
linux-user/s390x/vdso.S
Normal file
61
linux-user/s390x/vdso.S
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* s390x 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 raw_syscall n
|
||||||
|
.ifne \n < 0x100
|
||||||
|
svc \n
|
||||||
|
.else
|
||||||
|
lghi %r1, \n
|
||||||
|
svc 0
|
||||||
|
.endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro vdso_syscall name, nr
|
||||||
|
\name:
|
||||||
|
.cfi_startproc
|
||||||
|
aghi %r15, -(STACK_FRAME_OVERHEAD + 16)
|
||||||
|
.cfi_adjust_cfa_offset STACK_FRAME_OVERHEAD + 16
|
||||||
|
stg %r14, STACK_FRAME_OVERHEAD(%r15)
|
||||||
|
.cfi_rel_offset %r14, STACK_FRAME_OVERHEAD
|
||||||
|
raw_syscall \nr
|
||||||
|
lg %r14, STACK_FRAME_OVERHEAD(%r15)
|
||||||
|
aghi %r15, STACK_FRAME_OVERHEAD + 16
|
||||||
|
.cfi_restore %r14
|
||||||
|
.cfi_adjust_cfa_offset -(STACK_FRAME_OVERHEAD + 16)
|
||||||
|
br %r14
|
||||||
|
.cfi_endproc
|
||||||
|
endf \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO unwind info, though we're ok without it.
|
||||||
|
* The kernel supplies bogus empty unwind info, and it is likely ignored
|
||||||
|
* by all users. Without it we get the fallback signal frame handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
__kernel_sigreturn:
|
||||||
|
raw_syscall __NR_sigreturn
|
||||||
|
endf __kernel_sigreturn
|
||||||
|
|
||||||
|
__kernel_rt_sigreturn:
|
||||||
|
raw_syscall __NR_rt_sigreturn
|
||||||
|
endf __kernel_rt_sigreturn
|
72
linux-user/s390x/vdso.ld
Normal file
72
linux-user/s390x/vdso.ld
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for linux s390x replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
VERSION {
|
||||||
|
LINUX_2.6.29 {
|
||||||
|
global:
|
||||||
|
__kernel_gettimeofday;
|
||||||
|
__kernel_clock_gettime;
|
||||||
|
__kernel_clock_getres;
|
||||||
|
__kernel_getcpu;
|
||||||
|
__kernel_rt_sigreturn;
|
||||||
|
__kernel_sigreturn;
|
||||||
|
/*
|
||||||
|
* QEMU handles syscall restart internally, so we don't
|
||||||
|
* need the __kernel_restart_syscall entry point.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
BIN
linux-user/s390x/vdso.so
Executable file
BIN
linux-user/s390x/vdso.so
Executable file
Binary file not shown.
@ -536,11 +536,10 @@ static void signal_table_init(void)
|
|||||||
host_to_target_signal_table[SIGABRT] = 0;
|
host_to_target_signal_table[SIGABRT] = 0;
|
||||||
host_to_target_signal_table[hsig++] = TARGET_SIGABRT;
|
host_to_target_signal_table[hsig++] = TARGET_SIGABRT;
|
||||||
|
|
||||||
for (; hsig <= SIGRTMAX; hsig++) {
|
for (tsig = TARGET_SIGRTMIN;
|
||||||
tsig = hsig - SIGRTMIN + TARGET_SIGRTMIN;
|
hsig <= SIGRTMAX && tsig <= TARGET_NSIG;
|
||||||
if (tsig <= TARGET_NSIG) {
|
hsig++, tsig++) {
|
||||||
host_to_target_signal_table[hsig] = tsig;
|
host_to_target_signal_table[hsig] = tsig;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Invert the mapping that has already been assigned. */
|
/* Invert the mapping that has already been assigned. */
|
||||||
|
@ -7992,6 +7992,8 @@ static void open_self_maps_4(const struct open_self_maps_data *d,
|
|||||||
path = "[stack]";
|
path = "[stack]";
|
||||||
} else if (start == info->brk) {
|
} else if (start == info->brk) {
|
||||||
path = "[heap]";
|
path = "[heap]";
|
||||||
|
} else if (start == info->vdso) {
|
||||||
|
path = "[vdso]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Except null device (MAP_ANON), adjust offset for this fragment. */
|
/* Except null device (MAP_ANON), adjust offset for this fragment. */
|
||||||
|
11
linux-user/x86_64/Makefile.vdso
Normal file
11
linux-user/x86_64/Makefile.vdso
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
include $(BUILD_DIR)/tests/tcg/x86_64-linux-user/config-target.mak
|
||||||
|
|
||||||
|
SUBDIR = $(SRC_PATH)/linux-user/x86_64
|
||||||
|
VPATH += $(SUBDIR)
|
||||||
|
|
||||||
|
all: $(SUBDIR)/vdso.so
|
||||||
|
|
||||||
|
$(SUBDIR)/vdso.so: vdso.S vdso.ld
|
||||||
|
$(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso.so.1 \
|
||||||
|
-Wl,--build-id=sha1 -Wl,--hash-style=both \
|
||||||
|
-Wl,-T,$(SUBDIR)/vdso.ld $<
|
@ -3,3 +3,7 @@ syscall_nr_generators += {
|
|||||||
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ],
|
||||||
output: '@BASENAME@_nr.h')
|
output: '@BASENAME@_nr.h')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vdso_inc = gen_vdso.process('vdso.so')
|
||||||
|
|
||||||
|
linux_user_ss.add(when: 'TARGET_X86_64', if_true: vdso_inc)
|
||||||
|
78
linux-user/x86_64/vdso.S
Normal file
78
linux-user/x86_64/vdso.S
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* x86-64 linux replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
.macro endf name
|
||||||
|
.globl \name
|
||||||
|
.type \name, @function
|
||||||
|
.size \name, . - \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro weakalias name
|
||||||
|
\name = __vdso_\name
|
||||||
|
.weak \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro vdso_syscall name, nr
|
||||||
|
__vdso_\name:
|
||||||
|
mov $\nr, %eax
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
endf __vdso_\name
|
||||||
|
weakalias \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.cfi_startproc
|
||||||
|
|
||||||
|
vdso_syscall clock_gettime, __NR_clock_gettime
|
||||||
|
vdso_syscall clock_getres, __NR_clock_getres
|
||||||
|
vdso_syscall gettimeofday, __NR_gettimeofday
|
||||||
|
vdso_syscall time, __NR_time
|
||||||
|
|
||||||
|
__vdso_getcpu:
|
||||||
|
/*
|
||||||
|
* There is no syscall number for this allocated on x64.
|
||||||
|
* We can handle this several ways:
|
||||||
|
*
|
||||||
|
* (1) Invent a syscall number for use within qemu.
|
||||||
|
* It should be easy enough to pick a number that
|
||||||
|
* is well out of the way of the kernel numbers.
|
||||||
|
*
|
||||||
|
* (2) Force the emulated cpu to support the rdtscp insn,
|
||||||
|
* and initialize the TSC_AUX value the appropriate value.
|
||||||
|
*
|
||||||
|
* (3) Pretend that we're always running on cpu 0.
|
||||||
|
*
|
||||||
|
* This last is the one that's implemented here, with the
|
||||||
|
* tiny bit of extra code to support rdtscp in place.
|
||||||
|
*/
|
||||||
|
xor %ecx, %ecx /* rdtscp w/ tsc_aux = 0 */
|
||||||
|
|
||||||
|
/* if (cpu != NULL) *cpu = (ecx & 0xfff); */
|
||||||
|
test %rdi, %rdi
|
||||||
|
jz 1f
|
||||||
|
mov %ecx, %eax
|
||||||
|
and $0xfff, %eax
|
||||||
|
mov %eax, (%rdi)
|
||||||
|
|
||||||
|
/* if (node != NULL) *node = (ecx >> 12); */
|
||||||
|
1: test %rsi, %rsi
|
||||||
|
jz 2f
|
||||||
|
shr $12, %ecx
|
||||||
|
mov %ecx, (%rsi)
|
||||||
|
|
||||||
|
2: xor %eax, %eax
|
||||||
|
ret
|
||||||
|
endf __vdso_getcpu
|
||||||
|
|
||||||
|
weakalias getcpu
|
||||||
|
|
||||||
|
.cfi_endproc
|
||||||
|
|
||||||
|
/* TODO: Add elf note for LINUX_VERSION_CODE */
|
73
linux-user/x86_64/vdso.ld
Normal file
73
linux-user/x86_64/vdso.ld
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for linux x86-64 replacement vdso.
|
||||||
|
*
|
||||||
|
* Copyright 2023 Linaro, Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
VERSION {
|
||||||
|
LINUX_2.6 {
|
||||||
|
global:
|
||||||
|
clock_gettime;
|
||||||
|
__vdso_clock_gettime;
|
||||||
|
gettimeofday;
|
||||||
|
__vdso_gettimeofday;
|
||||||
|
getcpu;
|
||||||
|
__vdso_getcpu;
|
||||||
|
time;
|
||||||
|
__vdso_time;
|
||||||
|
clock_getres;
|
||||||
|
__vdso_clock_getres;
|
||||||
|
|
||||||
|
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/x86_64/vdso.so
Executable file
BIN
linux-user/x86_64/vdso.so
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user