diff options
author | Alex Smith <alex.smith@imgtec.com> | 2015-10-21 04:54:38 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-11-11 02:36:36 -0500 |
commit | ebb5e78cc63417a35254a791de66e1cc84f963cc (patch) | |
tree | 49814946abe594cb8e7a11ce8d0a5fdaabb49d31 | |
parent | 22773aa9b95657f0adc2b5342428d9da7a6d5d02 (diff) |
MIPS: Initial implementation of a VDSO
Add an initial implementation of a proper (i.e. an ELF shared library)
VDSO. With this commit it does not export any symbols, it only replaces
the current signal return trampoline page. A later commit will add user
implementations of gettimeofday()/clock_gettime().
To support both new toolchains and old ones which don't generate ABI
flags section, we define its content manually and then use a tool
(genvdso) to patch up the section to have the correct name and type.
genvdso also extracts symbol offsets ({,rt_}sigreturn) needed by the
kernel, and generates a C file containing a "struct mips_vdso_image"
containing both the VDSO data and these offsets. This C file is
compiled into the kernel.
On 64-bit kernels we require a different VDSO for each supported ABI,
so we may build up to 3 different VDSOs. The VDSO to use is selected by
the mips_abi structure.
A kernel/user shared data page is created and mapped below the VDSO
image. This is currently empty, but will be used by the user time
function implementations which are added later.
[markos.chandras@imgtec.com:
- Add more comments
- Move abi detection in genvdso.h since it's the get_symbol function
that needs it.
- Add an R6 specific way to calculate the base address of VDSO in order
to avoid the branch instruction which affects performance.
- Do not patch .gnu.attributes since it's not needed for dynamic linking.
- Simplify Makefile a little bit.
- checkpatch fixes
- Restrict VDSO support for binutils < 2.25 for pre-R6
- Include atomic64.h for O32 variant on MIPS64]
Signed-off-by: Alex Smith <alex.smith@imgtec.com>
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/11337/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/Kbuild | 1 | ||||
-rw-r--r-- | arch/mips/include/asm/abi.h | 5 | ||||
-rw-r--r-- | arch/mips/include/asm/elf.h | 7 | ||||
-rw-r--r-- | arch/mips/include/asm/processor.h | 8 | ||||
-rw-r--r-- | arch/mips/include/asm/vdso.h | 73 | ||||
-rw-r--r-- | arch/mips/include/uapi/asm/Kbuild | 2 | ||||
-rw-r--r-- | arch/mips/include/uapi/asm/auxvec.h | 17 | ||||
-rw-r--r-- | arch/mips/kernel/signal.c | 12 | ||||
-rw-r--r-- | arch/mips/kernel/signal32.c | 7 | ||||
-rw-r--r-- | arch/mips/kernel/signal_n32.c | 5 | ||||
-rw-r--r-- | arch/mips/kernel/vdso.c | 160 | ||||
-rw-r--r-- | arch/mips/vdso/.gitignore | 4 | ||||
-rw-r--r-- | arch/mips/vdso/Makefile | 160 | ||||
-rw-r--r-- | arch/mips/vdso/elf.S | 68 | ||||
-rw-r--r-- | arch/mips/vdso/genvdso.c | 293 | ||||
-rw-r--r-- | arch/mips/vdso/genvdso.h | 187 | ||||
-rw-r--r-- | arch/mips/vdso/sigreturn.S | 49 | ||||
-rw-r--r-- | arch/mips/vdso/vdso.h | 80 | ||||
-rw-r--r-- | arch/mips/vdso/vdso.lds.S | 100 |
19 files changed, 1114 insertions, 124 deletions
diff --git a/arch/mips/Kbuild b/arch/mips/Kbuild index dd295335891a..5c3f688a5232 100644 --- a/arch/mips/Kbuild +++ b/arch/mips/Kbuild | |||
@@ -17,6 +17,7 @@ obj- := $(platform-) | |||
17 | obj-y += kernel/ | 17 | obj-y += kernel/ |
18 | obj-y += mm/ | 18 | obj-y += mm/ |
19 | obj-y += net/ | 19 | obj-y += net/ |
20 | obj-y += vdso/ | ||
20 | 21 | ||
21 | ifdef CONFIG_KVM | 22 | ifdef CONFIG_KVM |
22 | obj-y += kvm/ | 23 | obj-y += kvm/ |
diff --git a/arch/mips/include/asm/abi.h b/arch/mips/include/asm/abi.h index 37f84078e78a..940760844e2f 100644 --- a/arch/mips/include/asm/abi.h +++ b/arch/mips/include/asm/abi.h | |||
@@ -11,19 +11,20 @@ | |||
11 | 11 | ||
12 | #include <asm/signal.h> | 12 | #include <asm/signal.h> |
13 | #include <asm/siginfo.h> | 13 | #include <asm/siginfo.h> |
14 | #include <asm/vdso.h> | ||
14 | 15 | ||
15 | struct mips_abi { | 16 | struct mips_abi { |
16 | int (* const setup_frame)(void *sig_return, struct ksignal *ksig, | 17 | int (* const setup_frame)(void *sig_return, struct ksignal *ksig, |
17 | struct pt_regs *regs, sigset_t *set); | 18 | struct pt_regs *regs, sigset_t *set); |
18 | const unsigned long signal_return_offset; | ||
19 | int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig, | 19 | int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig, |
20 | struct pt_regs *regs, sigset_t *set); | 20 | struct pt_regs *regs, sigset_t *set); |
21 | const unsigned long rt_signal_return_offset; | ||
22 | const unsigned long restart; | 21 | const unsigned long restart; |
23 | 22 | ||
24 | unsigned off_sc_fpregs; | 23 | unsigned off_sc_fpregs; |
25 | unsigned off_sc_fpc_csr; | 24 | unsigned off_sc_fpc_csr; |
26 | unsigned off_sc_used_math; | 25 | unsigned off_sc_used_math; |
26 | |||
27 | struct mips_vdso_image *vdso; | ||
27 | }; | 28 | }; |
28 | 29 | ||
29 | #endif /* _ASM_ABI_H */ | 30 | #endif /* _ASM_ABI_H */ |
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h index 53b26933b12c..b01a6ff468e0 100644 --- a/arch/mips/include/asm/elf.h +++ b/arch/mips/include/asm/elf.h | |||
@@ -8,6 +8,7 @@ | |||
8 | #ifndef _ASM_ELF_H | 8 | #ifndef _ASM_ELF_H |
9 | #define _ASM_ELF_H | 9 | #define _ASM_ELF_H |
10 | 10 | ||
11 | #include <linux/auxvec.h> | ||
11 | #include <linux/fs.h> | 12 | #include <linux/fs.h> |
12 | #include <uapi/linux/elf.h> | 13 | #include <uapi/linux/elf.h> |
13 | 14 | ||
@@ -419,6 +420,12 @@ extern const char *__elf_platform; | |||
419 | #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) | 420 | #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) |
420 | #endif | 421 | #endif |
421 | 422 | ||
423 | #define ARCH_DLINFO \ | ||
424 | do { \ | ||
425 | NEW_AUX_ENT(AT_SYSINFO_EHDR, \ | ||
426 | (unsigned long)current->mm->context.vdso); \ | ||
427 | } while (0) | ||
428 | |||
422 | #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 | 429 | #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 |
423 | struct linux_binprm; | 430 | struct linux_binprm; |
424 | extern int arch_setup_additional_pages(struct linux_binprm *bprm, | 431 | extern int arch_setup_additional_pages(struct linux_binprm *bprm, |
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h index 59ee6dcf6eed..3f832c3dd8f5 100644 --- a/arch/mips/include/asm/processor.h +++ b/arch/mips/include/asm/processor.h | |||
@@ -36,12 +36,6 @@ extern unsigned int vced_count, vcei_count; | |||
36 | */ | 36 | */ |
37 | #define HAVE_ARCH_PICK_MMAP_LAYOUT 1 | 37 | #define HAVE_ARCH_PICK_MMAP_LAYOUT 1 |
38 | 38 | ||
39 | /* | ||
40 | * A special page (the vdso) is mapped into all processes at the very | ||
41 | * top of the virtual memory space. | ||
42 | */ | ||
43 | #define SPECIAL_PAGES_SIZE PAGE_SIZE | ||
44 | |||
45 | #ifdef CONFIG_32BIT | 39 | #ifdef CONFIG_32BIT |
46 | #ifdef CONFIG_KVM_GUEST | 40 | #ifdef CONFIG_KVM_GUEST |
47 | /* User space process size is limited to 1GB in KVM Guest Mode */ | 41 | /* User space process size is limited to 1GB in KVM Guest Mode */ |
@@ -80,7 +74,7 @@ extern unsigned int vced_count, vcei_count; | |||
80 | 74 | ||
81 | #endif | 75 | #endif |
82 | 76 | ||
83 | #define STACK_TOP ((TASK_SIZE & PAGE_MASK) - SPECIAL_PAGES_SIZE) | 77 | #define STACK_TOP (TASK_SIZE & PAGE_MASK) |
84 | 78 | ||
85 | /* | 79 | /* |
86 | * This decides where the kernel will search for a free chunk of vm | 80 | * This decides where the kernel will search for a free chunk of vm |
diff --git a/arch/mips/include/asm/vdso.h b/arch/mips/include/asm/vdso.h index cca56aa40ff4..db2d45be8f2e 100644 --- a/arch/mips/include/asm/vdso.h +++ b/arch/mips/include/asm/vdso.h | |||
@@ -1,29 +1,70 @@ | |||
1 | /* | 1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | 2 | * Copyright (C) 2015 Imagination Technologies |
3 | * License. See the file "COPYING" in the main directory of this archive | 3 | * Author: Alex Smith <alex.smith@imgtec.com> |
4 | * for more details. | ||
5 | * | 4 | * |
6 | * Copyright (C) 2009 Cavium Networks | 5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
7 | */ | 9 | */ |
8 | 10 | ||
9 | #ifndef __ASM_VDSO_H | 11 | #ifndef __ASM_VDSO_H |
10 | #define __ASM_VDSO_H | 12 | #define __ASM_VDSO_H |
11 | 13 | ||
12 | #include <linux/types.h> | 14 | #include <linux/mm_types.h> |
13 | 15 | ||
16 | /** | ||
17 | * struct mips_vdso_image - Details of a VDSO image. | ||
18 | * @data: Pointer to VDSO image data (page-aligned). | ||
19 | * @size: Size of the VDSO image data (page-aligned). | ||
20 | * @off_sigreturn: Offset of the sigreturn() trampoline. | ||
21 | * @off_rt_sigreturn: Offset of the rt_sigreturn() trampoline. | ||
22 | * @mapping: Special mapping structure. | ||
23 | * | ||
24 | * This structure contains details of a VDSO image, including the image data | ||
25 | * and offsets of certain symbols required by the kernel. It is generated as | ||
26 | * part of the VDSO build process, aside from the mapping page array, which is | ||
27 | * populated at runtime. | ||
28 | */ | ||
29 | struct mips_vdso_image { | ||
30 | void *data; | ||
31 | unsigned long size; | ||
14 | 32 | ||
15 | #ifdef CONFIG_32BIT | 33 | unsigned long off_sigreturn; |
16 | struct mips_vdso { | 34 | unsigned long off_rt_sigreturn; |
17 | u32 signal_trampoline[2]; | 35 | |
18 | u32 rt_signal_trampoline[2]; | 36 | struct vm_special_mapping mapping; |
19 | }; | 37 | }; |
20 | #else /* !CONFIG_32BIT */ | 38 | |
21 | struct mips_vdso { | 39 | /* |
22 | u32 o32_signal_trampoline[2]; | 40 | * The following structures are auto-generated as part of the build for each |
23 | u32 o32_rt_signal_trampoline[2]; | 41 | * ABI by genvdso, see arch/mips/vdso/Makefile. |
24 | u32 rt_signal_trampoline[2]; | 42 | */ |
25 | u32 n32_rt_signal_trampoline[2]; | 43 | |
44 | extern struct mips_vdso_image vdso_image; | ||
45 | |||
46 | #ifdef CONFIG_MIPS32_O32 | ||
47 | extern struct mips_vdso_image vdso_image_o32; | ||
48 | #endif | ||
49 | |||
50 | #ifdef CONFIG_MIPS32_N32 | ||
51 | extern struct mips_vdso_image vdso_image_n32; | ||
52 | #endif | ||
53 | |||
54 | /** | ||
55 | * union mips_vdso_data - Data provided by the kernel for the VDSO. | ||
56 | * | ||
57 | * This structure contains data needed by functions within the VDSO. It is | ||
58 | * populated by the kernel and mapped read-only into user memory. | ||
59 | * | ||
60 | * Note: Care should be taken when modifying as the layout must remain the same | ||
61 | * for both 64- and 32-bit (for 32-bit userland on 64-bit kernel). | ||
62 | */ | ||
63 | union mips_vdso_data { | ||
64 | struct { | ||
65 | }; | ||
66 | |||
67 | u8 page[PAGE_SIZE]; | ||
26 | }; | 68 | }; |
27 | #endif /* CONFIG_32BIT */ | ||
28 | 69 | ||
29 | #endif /* __ASM_VDSO_H */ | 70 | #endif /* __ASM_VDSO_H */ |
diff --git a/arch/mips/include/uapi/asm/Kbuild b/arch/mips/include/uapi/asm/Kbuild index 96fe7395ed8d..f2cf41461146 100644 --- a/arch/mips/include/uapi/asm/Kbuild +++ b/arch/mips/include/uapi/asm/Kbuild | |||
@@ -1,9 +1,9 @@ | |||
1 | # UAPI Header export list | 1 | # UAPI Header export list |
2 | include include/uapi/asm-generic/Kbuild.asm | 2 | include include/uapi/asm-generic/Kbuild.asm |
3 | 3 | ||
4 | generic-y += auxvec.h | ||
5 | generic-y += ipcbuf.h | 4 | generic-y += ipcbuf.h |
6 | 5 | ||
6 | header-y += auxvec.h | ||
7 | header-y += bitfield.h | 7 | header-y += bitfield.h |
8 | header-y += bitsperlong.h | 8 | header-y += bitsperlong.h |
9 | header-y += break.h | 9 | header-y += break.h |
diff --git a/arch/mips/include/uapi/asm/auxvec.h b/arch/mips/include/uapi/asm/auxvec.h new file mode 100644 index 000000000000..c9c7195272c4 --- /dev/null +++ b/arch/mips/include/uapi/asm/auxvec.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #ifndef __ASM_AUXVEC_H | ||
12 | #define __ASM_AUXVEC_H | ||
13 | |||
14 | /* Location of VDSO image. */ | ||
15 | #define AT_SYSINFO_EHDR 33 | ||
16 | |||
17 | #endif /* __ASM_AUXVEC_H */ | ||
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 2fec67bfc457..bf792e2839a6 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -36,7 +36,6 @@ | |||
36 | #include <asm/ucontext.h> | 36 | #include <asm/ucontext.h> |
37 | #include <asm/cpu-features.h> | 37 | #include <asm/cpu-features.h> |
38 | #include <asm/war.h> | 38 | #include <asm/war.h> |
39 | #include <asm/vdso.h> | ||
40 | #include <asm/dsp.h> | 39 | #include <asm/dsp.h> |
41 | #include <asm/inst.h> | 40 | #include <asm/inst.h> |
42 | #include <asm/msa.h> | 41 | #include <asm/msa.h> |
@@ -752,16 +751,15 @@ static int setup_rt_frame(void *sig_return, struct ksignal *ksig, | |||
752 | struct mips_abi mips_abi = { | 751 | struct mips_abi mips_abi = { |
753 | #ifdef CONFIG_TRAD_SIGNALS | 752 | #ifdef CONFIG_TRAD_SIGNALS |
754 | .setup_frame = setup_frame, | 753 | .setup_frame = setup_frame, |
755 | .signal_return_offset = offsetof(struct mips_vdso, signal_trampoline), | ||
756 | #endif | 754 | #endif |
757 | .setup_rt_frame = setup_rt_frame, | 755 | .setup_rt_frame = setup_rt_frame, |
758 | .rt_signal_return_offset = | ||
759 | offsetof(struct mips_vdso, rt_signal_trampoline), | ||
760 | .restart = __NR_restart_syscall, | 756 | .restart = __NR_restart_syscall, |
761 | 757 | ||
762 | .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), | 758 | .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), |
763 | .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), | 759 | .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), |
764 | .off_sc_used_math = offsetof(struct sigcontext, sc_used_math), | 760 | .off_sc_used_math = offsetof(struct sigcontext, sc_used_math), |
761 | |||
762 | .vdso = &vdso_image, | ||
765 | }; | 763 | }; |
766 | 764 | ||
767 | static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) | 765 | static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) |
@@ -801,11 +799,11 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) | |||
801 | } | 799 | } |
802 | 800 | ||
803 | if (sig_uses_siginfo(&ksig->ka)) | 801 | if (sig_uses_siginfo(&ksig->ka)) |
804 | ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset, | 802 | ret = abi->setup_rt_frame(vdso + abi->vdso->off_rt_sigreturn, |
805 | ksig, regs, oldset); | 803 | ksig, regs, oldset); |
806 | else | 804 | else |
807 | ret = abi->setup_frame(vdso + abi->signal_return_offset, ksig, | 805 | ret = abi->setup_frame(vdso + abi->vdso->off_sigreturn, |
808 | regs, oldset); | 806 | ksig, regs, oldset); |
809 | 807 | ||
810 | signal_setup_done(ret, ksig, 0); | 808 | signal_setup_done(ret, ksig, 0); |
811 | } | 809 | } |
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index f7e89524e316..4909639aa35b 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c | |||
@@ -31,7 +31,6 @@ | |||
31 | #include <asm/ucontext.h> | 31 | #include <asm/ucontext.h> |
32 | #include <asm/fpu.h> | 32 | #include <asm/fpu.h> |
33 | #include <asm/war.h> | 33 | #include <asm/war.h> |
34 | #include <asm/vdso.h> | ||
35 | #include <asm/dsp.h> | 34 | #include <asm/dsp.h> |
36 | 35 | ||
37 | #include "signal-common.h" | 36 | #include "signal-common.h" |
@@ -406,14 +405,12 @@ static int setup_rt_frame_32(void *sig_return, struct ksignal *ksig, | |||
406 | */ | 405 | */ |
407 | struct mips_abi mips_abi_32 = { | 406 | struct mips_abi mips_abi_32 = { |
408 | .setup_frame = setup_frame_32, | 407 | .setup_frame = setup_frame_32, |
409 | .signal_return_offset = | ||
410 | offsetof(struct mips_vdso, o32_signal_trampoline), | ||
411 | .setup_rt_frame = setup_rt_frame_32, | 408 | .setup_rt_frame = setup_rt_frame_32, |
412 | .rt_signal_return_offset = | ||
413 | offsetof(struct mips_vdso, o32_rt_signal_trampoline), | ||
414 | .restart = __NR_O32_restart_syscall, | 409 | .restart = __NR_O32_restart_syscall, |
415 | 410 | ||
416 | .off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs), | 411 | .off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs), |
417 | .off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr), | 412 | .off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr), |
418 | .off_sc_used_math = offsetof(struct sigcontext32, sc_used_math), | 413 | .off_sc_used_math = offsetof(struct sigcontext32, sc_used_math), |
414 | |||
415 | .vdso = &vdso_image_o32, | ||
419 | }; | 416 | }; |
diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c index 0d017fdcaf07..a7bc38430500 100644 --- a/arch/mips/kernel/signal_n32.c +++ b/arch/mips/kernel/signal_n32.c | |||
@@ -38,7 +38,6 @@ | |||
38 | #include <asm/fpu.h> | 38 | #include <asm/fpu.h> |
39 | #include <asm/cpu-features.h> | 39 | #include <asm/cpu-features.h> |
40 | #include <asm/war.h> | 40 | #include <asm/war.h> |
41 | #include <asm/vdso.h> | ||
42 | 41 | ||
43 | #include "signal-common.h" | 42 | #include "signal-common.h" |
44 | 43 | ||
@@ -151,11 +150,11 @@ static int setup_rt_frame_n32(void *sig_return, struct ksignal *ksig, | |||
151 | 150 | ||
152 | struct mips_abi mips_abi_n32 = { | 151 | struct mips_abi mips_abi_n32 = { |
153 | .setup_rt_frame = setup_rt_frame_n32, | 152 | .setup_rt_frame = setup_rt_frame_n32, |
154 | .rt_signal_return_offset = | ||
155 | offsetof(struct mips_vdso, n32_rt_signal_trampoline), | ||
156 | .restart = __NR_N32_restart_syscall, | 153 | .restart = __NR_N32_restart_syscall, |
157 | 154 | ||
158 | .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), | 155 | .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), |
159 | .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), | 156 | .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), |
160 | .off_sc_used_math = offsetof(struct sigcontext, sc_used_math), | 157 | .off_sc_used_math = offsetof(struct sigcontext, sc_used_math), |
158 | |||
159 | .vdso = &vdso_image_n32, | ||
161 | }; | 160 | }; |
diff --git a/arch/mips/kernel/vdso.c b/arch/mips/kernel/vdso.c index ed2a278722a9..56cc3c4377fb 100644 --- a/arch/mips/kernel/vdso.c +++ b/arch/mips/kernel/vdso.c | |||
@@ -1,122 +1,116 @@ | |||
1 | /* | 1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | 2 | * Copyright (C) 2015 Imagination Technologies |
3 | * License. See the file "COPYING" in the main directory of this archive | 3 | * Author: Alex Smith <alex.smith@imgtec.com> |
4 | * for more details. | ||
5 | * | 4 | * |
6 | * Copyright (C) 2009, 2010 Cavium Networks, Inc. | 5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
7 | */ | 9 | */ |
8 | 10 | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/binfmts.h> | 11 | #include <linux/binfmts.h> |
16 | #include <linux/elf.h> | 12 | #include <linux/elf.h> |
17 | #include <linux/vmalloc.h> | 13 | #include <linux/err.h> |
18 | #include <linux/unistd.h> | 14 | #include <linux/init.h> |
19 | #include <linux/random.h> | 15 | #include <linux/mm.h> |
16 | #include <linux/sched.h> | ||
17 | #include <linux/slab.h> | ||
20 | 18 | ||
19 | #include <asm/abi.h> | ||
21 | #include <asm/vdso.h> | 20 | #include <asm/vdso.h> |
22 | #include <asm/uasm.h> | 21 | |
23 | #include <asm/processor.h> | 22 | /* Kernel-provided data used by the VDSO. */ |
23 | static union mips_vdso_data vdso_data __page_aligned_data; | ||
24 | 24 | ||
25 | /* | 25 | /* |
26 | * Including <asm/unistd.h> would give use the 64-bit syscall numbers ... | 26 | * Mapping for the VDSO data pages. The real pages are mapped manually, as |
27 | * what we map and where within the area they are mapped is determined at | ||
28 | * runtime. | ||
27 | */ | 29 | */ |
28 | #define __NR_O32_sigreturn 4119 | 30 | static struct page *no_pages[] = { NULL }; |
29 | #define __NR_O32_rt_sigreturn 4193 | 31 | static struct vm_special_mapping vdso_vvar_mapping = { |
30 | #define __NR_N32_rt_sigreturn 6211 | 32 | .name = "[vvar]", |
33 | .pages = no_pages, | ||
34 | }; | ||
31 | 35 | ||
32 | static struct page *vdso_page; | 36 | static void __init init_vdso_image(struct mips_vdso_image *image) |
33 | |||
34 | static void __init install_trampoline(u32 *tramp, unsigned int sigreturn) | ||
35 | { | 37 | { |
36 | uasm_i_addiu(&tramp, 2, 0, sigreturn); /* li v0, sigreturn */ | 38 | unsigned long num_pages, i; |
37 | uasm_i_syscall(&tramp, 0); | 39 | |
40 | BUG_ON(!PAGE_ALIGNED(image->data)); | ||
41 | BUG_ON(!PAGE_ALIGNED(image->size)); | ||
42 | |||
43 | num_pages = image->size / PAGE_SIZE; | ||
44 | |||
45 | for (i = 0; i < num_pages; i++) { | ||
46 | image->mapping.pages[i] = | ||
47 | virt_to_page(image->data + (i * PAGE_SIZE)); | ||
48 | } | ||
38 | } | 49 | } |
39 | 50 | ||
40 | static int __init init_vdso(void) | 51 | static int __init init_vdso(void) |
41 | { | 52 | { |
42 | struct mips_vdso *vdso; | 53 | init_vdso_image(&vdso_image); |
43 | 54 | ||
44 | vdso_page = alloc_page(GFP_KERNEL); | 55 | #ifdef CONFIG_MIPS32_O32 |
45 | if (!vdso_page) | 56 | init_vdso_image(&vdso_image_o32); |
46 | panic("Cannot allocate vdso"); | ||
47 | |||
48 | vdso = vmap(&vdso_page, 1, 0, PAGE_KERNEL); | ||
49 | if (!vdso) | ||
50 | panic("Cannot map vdso"); | ||
51 | clear_page(vdso); | ||
52 | |||
53 | install_trampoline(vdso->rt_signal_trampoline, __NR_rt_sigreturn); | ||
54 | #ifdef CONFIG_32BIT | ||
55 | install_trampoline(vdso->signal_trampoline, __NR_sigreturn); | ||
56 | #else | ||
57 | install_trampoline(vdso->n32_rt_signal_trampoline, | ||
58 | __NR_N32_rt_sigreturn); | ||
59 | install_trampoline(vdso->o32_signal_trampoline, __NR_O32_sigreturn); | ||
60 | install_trampoline(vdso->o32_rt_signal_trampoline, | ||
61 | __NR_O32_rt_sigreturn); | ||
62 | #endif | 57 | #endif |
63 | 58 | ||
64 | vunmap(vdso); | 59 | #ifdef CONFIG_MIPS32_N32 |
60 | init_vdso_image(&vdso_image_n32); | ||
61 | #endif | ||
65 | 62 | ||
66 | return 0; | 63 | return 0; |
67 | } | 64 | } |
68 | subsys_initcall(init_vdso); | 65 | subsys_initcall(init_vdso); |
69 | 66 | ||
70 | static unsigned long vdso_addr(unsigned long start) | ||
71 | { | ||
72 | unsigned long offset = 0UL; | ||
73 | |||
74 | if (current->flags & PF_RANDOMIZE) { | ||
75 | offset = get_random_int(); | ||
76 | offset <<= PAGE_SHIFT; | ||
77 | if (TASK_IS_32BIT_ADDR) | ||
78 | offset &= 0xfffffful; | ||
79 | else | ||
80 | offset &= 0xffffffful; | ||
81 | } | ||
82 | |||
83 | return STACK_TOP + offset; | ||
84 | } | ||
85 | |||
86 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | 67 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) |
87 | { | 68 | { |
88 | int ret; | 69 | struct mips_vdso_image *image = current->thread.abi->vdso; |
89 | unsigned long addr; | ||
90 | struct mm_struct *mm = current->mm; | 70 | struct mm_struct *mm = current->mm; |
71 | unsigned long base, vdso_addr; | ||
72 | struct vm_area_struct *vma; | ||
73 | int ret; | ||
91 | 74 | ||
92 | down_write(&mm->mmap_sem); | 75 | down_write(&mm->mmap_sem); |
93 | 76 | ||
94 | addr = vdso_addr(mm->start_stack); | 77 | base = get_unmapped_area(NULL, 0, PAGE_SIZE + image->size, 0, 0); |
95 | 78 | if (IS_ERR_VALUE(base)) { | |
96 | addr = get_unmapped_area(NULL, addr, PAGE_SIZE, 0, 0); | 79 | ret = base; |
97 | if (IS_ERR_VALUE(addr)) { | 80 | goto out; |
98 | ret = addr; | ||
99 | goto up_fail; | ||
100 | } | 81 | } |
101 | 82 | ||
102 | ret = install_special_mapping(mm, addr, PAGE_SIZE, | 83 | vdso_addr = base + PAGE_SIZE; |
103 | VM_READ|VM_EXEC| | 84 | |
104 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | 85 | vma = _install_special_mapping(mm, base, PAGE_SIZE, |
105 | &vdso_page); | 86 | VM_READ | VM_MAYREAD, |
87 | &vdso_vvar_mapping); | ||
88 | if (IS_ERR(vma)) { | ||
89 | ret = PTR_ERR(vma); | ||
90 | goto out; | ||
91 | } | ||
106 | 92 | ||
93 | /* Map data page. */ | ||
94 | ret = remap_pfn_range(vma, base, | ||
95 | virt_to_phys(&vdso_data) >> PAGE_SHIFT, | ||
96 | PAGE_SIZE, PAGE_READONLY); | ||
107 | if (ret) | 97 | if (ret) |
108 | goto up_fail; | 98 | goto out; |
99 | |||
100 | /* Map VDSO image. */ | ||
101 | vma = _install_special_mapping(mm, vdso_addr, image->size, | ||
102 | VM_READ | VM_EXEC | | ||
103 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | ||
104 | &image->mapping); | ||
105 | if (IS_ERR(vma)) { | ||
106 | ret = PTR_ERR(vma); | ||
107 | goto out; | ||
108 | } | ||
109 | 109 | ||
110 | mm->context.vdso = (void *)addr; | 110 | mm->context.vdso = (void *)vdso_addr; |
111 | ret = 0; | ||
111 | 112 | ||
112 | up_fail: | 113 | out: |
113 | up_write(&mm->mmap_sem); | 114 | up_write(&mm->mmap_sem); |
114 | return ret; | 115 | return ret; |
115 | } | 116 | } |
116 | |||
117 | const char *arch_vma_name(struct vm_area_struct *vma) | ||
118 | { | ||
119 | if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) | ||
120 | return "[vdso]"; | ||
121 | return NULL; | ||
122 | } | ||
diff --git a/arch/mips/vdso/.gitignore b/arch/mips/vdso/.gitignore new file mode 100644 index 000000000000..5286a7d73d79 --- /dev/null +++ b/arch/mips/vdso/.gitignore | |||
@@ -0,0 +1,4 @@ | |||
1 | *.so* | ||
2 | vdso-*image.c | ||
3 | genvdso | ||
4 | vdso*.lds | ||
diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile new file mode 100644 index 000000000000..ef5f348f386a --- /dev/null +++ b/arch/mips/vdso/Makefile | |||
@@ -0,0 +1,160 @@ | |||
1 | # Objects to go into the VDSO. | ||
2 | obj-vdso-y := elf.o gettimeofday.o sigreturn.o | ||
3 | |||
4 | # Common compiler flags between ABIs. | ||
5 | ccflags-vdso := \ | ||
6 | $(filter -I%,$(KBUILD_CFLAGS)) \ | ||
7 | $(filter -E%,$(KBUILD_CFLAGS)) \ | ||
8 | $(filter -march=%,$(KBUILD_CFLAGS)) | ||
9 | cflags-vdso := $(ccflags-vdso) \ | ||
10 | $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \ | ||
11 | -O2 -g -fPIC -fno-common -fno-builtin -G 0 -DDISABLE_BRANCH_PROFILING \ | ||
12 | $(call cc-option, -fno-stack-protector) | ||
13 | aflags-vdso := $(ccflags-vdso) \ | ||
14 | $(filter -I%,$(KBUILD_CFLAGS)) \ | ||
15 | $(filter -E%,$(KBUILD_CFLAGS)) \ | ||
16 | -D__ASSEMBLY__ -Wa,-gdwarf-2 | ||
17 | |||
18 | # | ||
19 | # For the pre-R6 code in arch/mips/vdso/vdso.h for locating | ||
20 | # the base address of VDSO, the linker will emit a R_MIPS_PC32 | ||
21 | # relocation in binutils > 2.25 but it will fail with older versions | ||
22 | # because that relocation is not supported for that symbol. As a result | ||
23 | # of which we are forced to disable the VDSO symbols when building | ||
24 | # with < 2.25 binutils on pre-R6 kernels. For more references on why we | ||
25 | # can't use other methods to get the base address of VDSO please refer to | ||
26 | # the comments on that file. | ||
27 | # | ||
28 | ifndef CONFIG_CPU_MIPSR6 | ||
29 | ifeq ($(call ld-ifversion, -gt, 22400000, y),) | ||
30 | $(warning MIPS VDSO requires binutils > 2.24) | ||
31 | obj-vdso-y := $(filter-out gettimeofday.o, $(obj-vdso-y)) | ||
32 | ccflags-vdso += -DDISABLE_MIPS_VDSO | ||
33 | endif | ||
34 | endif | ||
35 | |||
36 | # VDSO linker flags. | ||
37 | VDSO_LDFLAGS := \ | ||
38 | -Wl,-Bsymbolic -Wl,--no-undefined -Wl,-soname=linux-vdso.so.1 \ | ||
39 | -nostdlib -shared \ | ||
40 | $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) \ | ||
41 | $(call cc-ldoption, -Wl$(comma)--build-id) | ||
42 | |||
43 | GCOV_PROFILE := n | ||
44 | |||
45 | # | ||
46 | # Shared build commands. | ||
47 | # | ||
48 | |||
49 | quiet_cmd_vdsold = VDSO $@ | ||
50 | cmd_vdsold = $(CC) $(c_flags) $(VDSO_LDFLAGS) \ | ||
51 | -Wl,-T $(filter %.lds,$^) $(filter %.o,$^) -o $@ | ||
52 | |||
53 | hostprogs-y := genvdso | ||
54 | |||
55 | quiet_cmd_genvdso = GENVDSO $@ | ||
56 | define cmd_genvdso | ||
57 | cp $< $(<:%.dbg=%) && \ | ||
58 | $(OBJCOPY) -S $< $(<:%.dbg=%) && \ | ||
59 | $(obj)/genvdso $< $(<:%.dbg=%) $@ $(VDSO_NAME) | ||
60 | endef | ||
61 | |||
62 | # | ||
63 | # Build native VDSO. | ||
64 | # | ||
65 | |||
66 | native-abi := $(filter -mabi=%,$(KBUILD_CFLAGS)) | ||
67 | |||
68 | targets += $(obj-vdso-y) | ||
69 | targets += vdso.lds vdso.so.dbg vdso.so vdso-image.c | ||
70 | |||
71 | obj-vdso := $(obj-vdso-y:%.o=$(obj)/%.o) | ||
72 | |||
73 | $(obj-vdso): KBUILD_CFLAGS := $(cflags-vdso) $(native-abi) | ||
74 | $(obj-vdso): KBUILD_AFLAGS := $(aflags-vdso) $(native-abi) | ||
75 | |||
76 | $(obj)/vdso.lds: KBUILD_CPPFLAGS := $(native-abi) | ||
77 | |||
78 | $(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE | ||
79 | $(call if_changed,vdsold) | ||
80 | |||
81 | $(obj)/vdso-image.c: $(obj)/vdso.so.dbg $(obj)/genvdso FORCE | ||
82 | $(call if_changed,genvdso) | ||
83 | |||
84 | obj-y += vdso-image.o | ||
85 | |||
86 | # | ||
87 | # Build O32 VDSO. | ||
88 | # | ||
89 | |||
90 | # Define these outside the ifdef to ensure they are picked up by clean. | ||
91 | targets += $(obj-vdso-y:%.o=%-o32.o) | ||
92 | targets += vdso-o32.lds vdso-o32.so.dbg vdso-o32.so vdso-o32-image.c | ||
93 | |||
94 | ifdef CONFIG_MIPS32_O32 | ||
95 | |||
96 | obj-vdso-o32 := $(obj-vdso-y:%.o=$(obj)/%-o32.o) | ||
97 | |||
98 | $(obj-vdso-o32): KBUILD_CFLAGS := $(cflags-vdso) -mabi=32 | ||
99 | $(obj-vdso-o32): KBUILD_AFLAGS := $(aflags-vdso) -mabi=32 | ||
100 | |||
101 | $(obj)/%-o32.o: $(src)/%.S FORCE | ||
102 | $(call if_changed_dep,as_o_S) | ||
103 | |||
104 | $(obj)/%-o32.o: $(src)/%.c FORCE | ||
105 | $(call cmd,force_checksrc) | ||
106 | $(call if_changed_rule,cc_o_c) | ||
107 | |||
108 | $(obj)/vdso-o32.lds: KBUILD_CPPFLAGS := -mabi=32 | ||
109 | $(obj)/vdso-o32.lds: $(src)/vdso.lds.S FORCE | ||
110 | $(call if_changed_dep,cpp_lds_S) | ||
111 | |||
112 | $(obj)/vdso-o32.so.dbg: $(obj)/vdso-o32.lds $(obj-vdso-o32) FORCE | ||
113 | $(call if_changed,vdsold) | ||
114 | |||
115 | $(obj)/vdso-o32-image.c: VDSO_NAME := o32 | ||
116 | $(obj)/vdso-o32-image.c: $(obj)/vdso-o32.so.dbg $(obj)/genvdso FORCE | ||
117 | $(call if_changed,genvdso) | ||
118 | |||
119 | obj-y += vdso-o32-image.o | ||
120 | |||
121 | endif | ||
122 | |||
123 | # | ||
124 | # Build N32 VDSO. | ||
125 | # | ||
126 | |||
127 | targets += $(obj-vdso-y:%.o=%-n32.o) | ||
128 | targets += vdso-n32.lds vdso-n32.so.dbg vdso-n32.so vdso-n32-image.c | ||
129 | |||
130 | ifdef CONFIG_MIPS32_N32 | ||
131 | |||
132 | obj-vdso-n32 := $(obj-vdso-y:%.o=$(obj)/%-n32.o) | ||
133 | |||
134 | $(obj-vdso-n32): KBUILD_CFLAGS := $(cflags-vdso) -mabi=n32 | ||
135 | $(obj-vdso-n32): KBUILD_AFLAGS := $(aflags-vdso) -mabi=n32 | ||
136 | |||
137 | $(obj)/%-n32.o: $(src)/%.S FORCE | ||
138 | $(call if_changed_dep,as_o_S) | ||
139 | |||
140 | $(obj)/%-n32.o: $(src)/%.c FORCE | ||
141 | $(call cmd,force_checksrc) | ||
142 | $(call if_changed_rule,cc_o_c) | ||
143 | |||
144 | $(obj)/vdso-n32.lds: KBUILD_CPPFLAGS := -mabi=n32 | ||
145 | $(obj)/vdso-n32.lds: $(src)/vdso.lds.S FORCE | ||
146 | $(call if_changed_dep,cpp_lds_S) | ||
147 | |||
148 | $(obj)/vdso-n32.so.dbg: $(obj)/vdso-n32.lds $(obj-vdso-n32) FORCE | ||
149 | $(call if_changed,vdsold) | ||
150 | |||
151 | $(obj)/vdso-n32-image.c: VDSO_NAME := n32 | ||
152 | $(obj)/vdso-n32-image.c: $(obj)/vdso-n32.so.dbg $(obj)/genvdso FORCE | ||
153 | $(call if_changed,genvdso) | ||
154 | |||
155 | obj-y += vdso-n32-image.o | ||
156 | |||
157 | endif | ||
158 | |||
159 | # FIXME: Need install rule for debug. | ||
160 | # Needs to deal with dependency for generation of dbg by cmd_genvdso... | ||
diff --git a/arch/mips/vdso/elf.S b/arch/mips/vdso/elf.S new file mode 100644 index 000000000000..be37bbb1f061 --- /dev/null +++ b/arch/mips/vdso/elf.S | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include "vdso.h" | ||
12 | |||
13 | #include <linux/elfnote.h> | ||
14 | #include <linux/version.h> | ||
15 | |||
16 | ELFNOTE_START(Linux, 0, "a") | ||
17 | .long LINUX_VERSION_CODE | ||
18 | ELFNOTE_END | ||
19 | |||
20 | /* | ||
21 | * The .MIPS.abiflags section must be defined with the FP ABI flags set | ||
22 | * to 'any' to be able to link with both old and new libraries. | ||
23 | * Newer toolchains are capable of automatically generating this, but we want | ||
24 | * to work with older toolchains as well. Therefore, we define the contents of | ||
25 | * this section here (under different names), and then genvdso will patch | ||
26 | * it to have the correct name and type. | ||
27 | * | ||
28 | * We base the .MIPS.abiflags section on preprocessor definitions rather than | ||
29 | * CONFIG_* because we need to match the particular ABI we are building the | ||
30 | * VDSO for. | ||
31 | * | ||
32 | * See https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking | ||
33 | * for the .MIPS.abiflags section description. | ||
34 | */ | ||
35 | |||
36 | .section .mips_abiflags, "a" | ||
37 | .align 3 | ||
38 | __mips_abiflags: | ||
39 | .hword 0 /* version */ | ||
40 | .byte __mips /* isa_level */ | ||
41 | |||
42 | /* isa_rev */ | ||
43 | #ifdef __mips_isa_rev | ||
44 | .byte __mips_isa_rev | ||
45 | #else | ||
46 | .byte 0 | ||
47 | #endif | ||
48 | |||
49 | /* gpr_size */ | ||
50 | #ifdef __mips64 | ||
51 | .byte 2 /* AFL_REG_64 */ | ||
52 | #else | ||
53 | .byte 1 /* AFL_REG_32 */ | ||
54 | #endif | ||
55 | |||
56 | /* cpr1_size */ | ||
57 | #if (defined(__mips_isa_rev) && __mips_isa_rev >= 6) || defined(__mips64) | ||
58 | .byte 2 /* AFL_REG_64 */ | ||
59 | #else | ||
60 | .byte 1 /* AFL_REG_32 */ | ||
61 | #endif | ||
62 | |||
63 | .byte 0 /* cpr2_size (AFL_REG_NONE) */ | ||
64 | .byte 0 /* fp_abi (Val_GNU_MIPS_ABI_FP_ANY) */ | ||
65 | .word 0 /* isa_ext */ | ||
66 | .word 0 /* ases */ | ||
67 | .word 0 /* flags1 */ | ||
68 | .word 0 /* flags2 */ | ||
diff --git a/arch/mips/vdso/genvdso.c b/arch/mips/vdso/genvdso.c new file mode 100644 index 000000000000..530a36f465ce --- /dev/null +++ b/arch/mips/vdso/genvdso.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * This tool is used to generate the real VDSO images from the raw image. It | ||
13 | * first patches up the MIPS ABI flags and GNU attributes sections defined in | ||
14 | * elf.S to have the correct name and type. It then generates a C source file | ||
15 | * to be compiled into the kernel containing the VDSO image data and a | ||
16 | * mips_vdso_image struct for it, including symbol offsets extracted from the | ||
17 | * image. | ||
18 | * | ||
19 | * We need to be passed both a stripped and unstripped VDSO image. The stripped | ||
20 | * image is compiled into the kernel, but we must also patch up the unstripped | ||
21 | * image's ABI flags sections so that it can be installed and used for | ||
22 | * debugging. | ||
23 | */ | ||
24 | |||
25 | #include <sys/mman.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <sys/types.h> | ||
28 | |||
29 | #include <byteswap.h> | ||
30 | #include <elf.h> | ||
31 | #include <errno.h> | ||
32 | #include <fcntl.h> | ||
33 | #include <inttypes.h> | ||
34 | #include <stdarg.h> | ||
35 | #include <stdbool.h> | ||
36 | #include <stdio.h> | ||
37 | #include <stdlib.h> | ||
38 | #include <string.h> | ||
39 | #include <unistd.h> | ||
40 | |||
41 | /* Define these in case the system elf.h is not new enough to have them. */ | ||
42 | #ifndef SHT_GNU_ATTRIBUTES | ||
43 | # define SHT_GNU_ATTRIBUTES 0x6ffffff5 | ||
44 | #endif | ||
45 | #ifndef SHT_MIPS_ABIFLAGS | ||
46 | # define SHT_MIPS_ABIFLAGS 0x7000002a | ||
47 | #endif | ||
48 | |||
49 | enum { | ||
50 | ABI_O32 = (1 << 0), | ||
51 | ABI_N32 = (1 << 1), | ||
52 | ABI_N64 = (1 << 2), | ||
53 | |||
54 | ABI_ALL = ABI_O32 | ABI_N32 | ABI_N64, | ||
55 | }; | ||
56 | |||
57 | /* Symbols the kernel requires offsets for. */ | ||
58 | static struct { | ||
59 | const char *name; | ||
60 | const char *offset_name; | ||
61 | unsigned int abis; | ||
62 | } vdso_symbols[] = { | ||
63 | { "__vdso_sigreturn", "off_sigreturn", ABI_O32 }, | ||
64 | { "__vdso_rt_sigreturn", "off_rt_sigreturn", ABI_ALL }, | ||
65 | {} | ||
66 | }; | ||
67 | |||
68 | static const char *program_name; | ||
69 | static const char *vdso_name; | ||
70 | static unsigned char elf_class; | ||
71 | static unsigned int elf_abi; | ||
72 | static bool need_swap; | ||
73 | static FILE *out_file; | ||
74 | |||
75 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | ||
76 | # define HOST_ORDER ELFDATA2LSB | ||
77 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | ||
78 | # define HOST_ORDER ELFDATA2MSB | ||
79 | #endif | ||
80 | |||
81 | #define BUILD_SWAP(bits) \ | ||
82 | static uint##bits##_t swap_uint##bits(uint##bits##_t val) \ | ||
83 | { \ | ||
84 | return need_swap ? bswap_##bits(val) : val; \ | ||
85 | } | ||
86 | |||
87 | BUILD_SWAP(16) | ||
88 | BUILD_SWAP(32) | ||
89 | BUILD_SWAP(64) | ||
90 | |||
91 | #define __FUNC(name, bits) name##bits | ||
92 | #define _FUNC(name, bits) __FUNC(name, bits) | ||
93 | #define FUNC(name) _FUNC(name, ELF_BITS) | ||
94 | |||
95 | #define __ELF(x, bits) Elf##bits##_##x | ||
96 | #define _ELF(x, bits) __ELF(x, bits) | ||
97 | #define ELF(x) _ELF(x, ELF_BITS) | ||
98 | |||
99 | /* | ||
100 | * Include genvdso.h twice with ELF_BITS defined differently to get functions | ||
101 | * for both ELF32 and ELF64. | ||
102 | */ | ||
103 | |||
104 | #define ELF_BITS 64 | ||
105 | #include "genvdso.h" | ||
106 | #undef ELF_BITS | ||
107 | |||
108 | #define ELF_BITS 32 | ||
109 | #include "genvdso.h" | ||
110 | #undef ELF_BITS | ||
111 | |||
112 | static void *map_vdso(const char *path, size_t *_size) | ||
113 | { | ||
114 | int fd; | ||
115 | struct stat stat; | ||
116 | void *addr; | ||
117 | const Elf32_Ehdr *ehdr; | ||
118 | |||
119 | fd = open(path, O_RDWR); | ||
120 | if (fd < 0) { | ||
121 | fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name, | ||
122 | path, strerror(errno)); | ||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | if (fstat(fd, &stat) != 0) { | ||
127 | fprintf(stderr, "%s: Failed to stat '%s': %s\n", program_name, | ||
128 | path, strerror(errno)); | ||
129 | return NULL; | ||
130 | } | ||
131 | |||
132 | addr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, | ||
133 | 0); | ||
134 | if (addr == MAP_FAILED) { | ||
135 | fprintf(stderr, "%s: Failed to map '%s': %s\n", program_name, | ||
136 | path, strerror(errno)); | ||
137 | return NULL; | ||
138 | } | ||
139 | |||
140 | /* ELF32/64 header formats are the same for the bits we're checking. */ | ||
141 | ehdr = addr; | ||
142 | |||
143 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { | ||
144 | fprintf(stderr, "%s: '%s' is not an ELF file\n", program_name, | ||
145 | path); | ||
146 | return NULL; | ||
147 | } | ||
148 | |||
149 | elf_class = ehdr->e_ident[EI_CLASS]; | ||
150 | switch (elf_class) { | ||
151 | case ELFCLASS32: | ||
152 | case ELFCLASS64: | ||
153 | break; | ||
154 | default: | ||
155 | fprintf(stderr, "%s: '%s' has invalid ELF class\n", | ||
156 | program_name, path); | ||
157 | return NULL; | ||
158 | } | ||
159 | |||
160 | switch (ehdr->e_ident[EI_DATA]) { | ||
161 | case ELFDATA2LSB: | ||
162 | case ELFDATA2MSB: | ||
163 | need_swap = ehdr->e_ident[EI_DATA] != HOST_ORDER; | ||
164 | break; | ||
165 | default: | ||
166 | fprintf(stderr, "%s: '%s' has invalid ELF data order\n", | ||
167 | program_name, path); | ||
168 | return NULL; | ||
169 | } | ||
170 | |||
171 | if (swap_uint16(ehdr->e_machine) != EM_MIPS) { | ||
172 | fprintf(stderr, | ||
173 | "%s: '%s' has invalid ELF machine (expected EM_MIPS)\n", | ||
174 | program_name, path); | ||
175 | return NULL; | ||
176 | } else if (swap_uint16(ehdr->e_type) != ET_DYN) { | ||
177 | fprintf(stderr, | ||
178 | "%s: '%s' has invalid ELF type (expected ET_DYN)\n", | ||
179 | program_name, path); | ||
180 | return NULL; | ||
181 | } | ||
182 | |||
183 | *_size = stat.st_size; | ||
184 | return addr; | ||
185 | } | ||
186 | |||
187 | static bool patch_vdso(const char *path, void *vdso) | ||
188 | { | ||
189 | if (elf_class == ELFCLASS64) | ||
190 | return patch_vdso64(path, vdso); | ||
191 | else | ||
192 | return patch_vdso32(path, vdso); | ||
193 | } | ||
194 | |||
195 | static bool get_symbols(const char *path, void *vdso) | ||
196 | { | ||
197 | if (elf_class == ELFCLASS64) | ||
198 | return get_symbols64(path, vdso); | ||
199 | else | ||
200 | return get_symbols32(path, vdso); | ||
201 | } | ||
202 | |||
203 | int main(int argc, char **argv) | ||
204 | { | ||
205 | const char *dbg_vdso_path, *vdso_path, *out_path; | ||
206 | void *dbg_vdso, *vdso; | ||
207 | size_t dbg_vdso_size, vdso_size, i; | ||
208 | |||
209 | program_name = argv[0]; | ||
210 | |||
211 | if (argc < 4 || argc > 5) { | ||
212 | fprintf(stderr, | ||
213 | "Usage: %s <debug VDSO> <stripped VDSO> <output file> [<name>]\n", | ||
214 | program_name); | ||
215 | return EXIT_FAILURE; | ||
216 | } | ||
217 | |||
218 | dbg_vdso_path = argv[1]; | ||
219 | vdso_path = argv[2]; | ||
220 | out_path = argv[3]; | ||
221 | vdso_name = (argc > 4) ? argv[4] : ""; | ||
222 | |||
223 | dbg_vdso = map_vdso(dbg_vdso_path, &dbg_vdso_size); | ||
224 | if (!dbg_vdso) | ||
225 | return EXIT_FAILURE; | ||
226 | |||
227 | vdso = map_vdso(vdso_path, &vdso_size); | ||
228 | if (!vdso) | ||
229 | return EXIT_FAILURE; | ||
230 | |||
231 | /* Patch both the VDSOs' ABI flags sections. */ | ||
232 | if (!patch_vdso(dbg_vdso_path, dbg_vdso)) | ||
233 | return EXIT_FAILURE; | ||
234 | if (!patch_vdso(vdso_path, vdso)) | ||
235 | return EXIT_FAILURE; | ||
236 | |||
237 | if (msync(dbg_vdso, dbg_vdso_size, MS_SYNC) != 0) { | ||
238 | fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name, | ||
239 | dbg_vdso_path, strerror(errno)); | ||
240 | return EXIT_FAILURE; | ||
241 | } else if (msync(vdso, vdso_size, MS_SYNC) != 0) { | ||
242 | fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name, | ||
243 | vdso_path, strerror(errno)); | ||
244 | return EXIT_FAILURE; | ||
245 | } | ||
246 | |||
247 | out_file = fopen(out_path, "w"); | ||
248 | if (!out_file) { | ||
249 | fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name, | ||
250 | out_path, strerror(errno)); | ||
251 | return EXIT_FAILURE; | ||
252 | } | ||
253 | |||
254 | fprintf(out_file, "/* Automatically generated - do not edit */\n"); | ||
255 | fprintf(out_file, "#include <linux/linkage.h>\n"); | ||
256 | fprintf(out_file, "#include <linux/mm.h>\n"); | ||
257 | fprintf(out_file, "#include <asm/vdso.h>\n"); | ||
258 | |||
259 | /* Write out the stripped VDSO data. */ | ||
260 | fprintf(out_file, | ||
261 | "static unsigned char vdso_data[PAGE_ALIGN(%zu)] __page_aligned_data = {\n\t", | ||
262 | vdso_size); | ||
263 | for (i = 0; i < vdso_size; i++) { | ||
264 | if (!(i % 10)) | ||
265 | fprintf(out_file, "\n\t"); | ||
266 | fprintf(out_file, "0x%02x, ", ((unsigned char *)vdso)[i]); | ||
267 | } | ||
268 | fprintf(out_file, "\n};\n"); | ||
269 | |||
270 | /* Preallocate a page array. */ | ||
271 | fprintf(out_file, | ||
272 | "static struct page *vdso_pages[PAGE_ALIGN(%zu) / PAGE_SIZE];\n", | ||
273 | vdso_size); | ||
274 | |||
275 | fprintf(out_file, "struct mips_vdso_image vdso_image%s%s = {\n", | ||
276 | (vdso_name[0]) ? "_" : "", vdso_name); | ||
277 | fprintf(out_file, "\t.data = vdso_data,\n"); | ||
278 | fprintf(out_file, "\t.size = PAGE_ALIGN(%zu),\n", vdso_size); | ||
279 | fprintf(out_file, "\t.mapping = {\n"); | ||
280 | fprintf(out_file, "\t\t.name = \"[vdso]\",\n"); | ||
281 | fprintf(out_file, "\t\t.pages = vdso_pages,\n"); | ||
282 | fprintf(out_file, "\t},\n"); | ||
283 | |||
284 | /* Calculate and write symbol offsets to <output file> */ | ||
285 | if (!get_symbols(dbg_vdso_path, dbg_vdso)) { | ||
286 | unlink(out_path); | ||
287 | return EXIT_FAILURE; | ||
288 | } | ||
289 | |||
290 | fprintf(out_file, "};\n"); | ||
291 | |||
292 | return EXIT_SUCCESS; | ||
293 | } | ||
diff --git a/arch/mips/vdso/genvdso.h b/arch/mips/vdso/genvdso.h new file mode 100644 index 000000000000..94334727059a --- /dev/null +++ b/arch/mips/vdso/genvdso.h | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | static inline bool FUNC(patch_vdso)(const char *path, void *vdso) | ||
12 | { | ||
13 | const ELF(Ehdr) *ehdr = vdso; | ||
14 | void *shdrs; | ||
15 | ELF(Shdr) *shdr; | ||
16 | char *shstrtab, *name; | ||
17 | uint16_t sh_count, sh_entsize, i; | ||
18 | unsigned int local_gotno, symtabno, gotsym; | ||
19 | ELF(Dyn) *dyn = NULL; | ||
20 | |||
21 | shdrs = vdso + FUNC(swap_uint)(ehdr->e_shoff); | ||
22 | sh_count = swap_uint16(ehdr->e_shnum); | ||
23 | sh_entsize = swap_uint16(ehdr->e_shentsize); | ||
24 | |||
25 | shdr = shdrs + (sh_entsize * swap_uint16(ehdr->e_shstrndx)); | ||
26 | shstrtab = vdso + FUNC(swap_uint)(shdr->sh_offset); | ||
27 | |||
28 | for (i = 0; i < sh_count; i++) { | ||
29 | shdr = shdrs + (i * sh_entsize); | ||
30 | name = shstrtab + swap_uint32(shdr->sh_name); | ||
31 | |||
32 | /* | ||
33 | * Ensure there are no relocation sections - ld.so does not | ||
34 | * relocate the VDSO so if there are relocations things will | ||
35 | * break. | ||
36 | */ | ||
37 | switch (swap_uint32(shdr->sh_type)) { | ||
38 | case SHT_REL: | ||
39 | case SHT_RELA: | ||
40 | fprintf(stderr, | ||
41 | "%s: '%s' contains relocation sections\n", | ||
42 | program_name, path); | ||
43 | return false; | ||
44 | case SHT_DYNAMIC: | ||
45 | dyn = vdso + FUNC(swap_uint)(shdr->sh_offset); | ||
46 | break; | ||
47 | } | ||
48 | |||
49 | /* Check for existing sections. */ | ||
50 | if (strcmp(name, ".MIPS.abiflags") == 0) { | ||
51 | fprintf(stderr, | ||
52 | "%s: '%s' already contains a '.MIPS.abiflags' section\n", | ||
53 | program_name, path); | ||
54 | return false; | ||
55 | } | ||
56 | |||
57 | if (strcmp(name, ".mips_abiflags") == 0) { | ||
58 | strcpy(name, ".MIPS.abiflags"); | ||
59 | shdr->sh_type = swap_uint32(SHT_MIPS_ABIFLAGS); | ||
60 | shdr->sh_entsize = shdr->sh_size; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Ensure the GOT has no entries other than the standard 2, for the same | ||
66 | * reason we check that there's no relocation sections above. | ||
67 | * The standard two entries are: | ||
68 | * - Lazy resolver | ||
69 | * - Module pointer | ||
70 | */ | ||
71 | if (dyn) { | ||
72 | local_gotno = symtabno = gotsym = 0; | ||
73 | |||
74 | while (FUNC(swap_uint)(dyn->d_tag) != DT_NULL) { | ||
75 | switch (FUNC(swap_uint)(dyn->d_tag)) { | ||
76 | /* | ||
77 | * This member holds the number of local GOT entries. | ||
78 | */ | ||
79 | case DT_MIPS_LOCAL_GOTNO: | ||
80 | local_gotno = FUNC(swap_uint)(dyn->d_un.d_val); | ||
81 | break; | ||
82 | /* | ||
83 | * This member holds the number of entries in the | ||
84 | * .dynsym section. | ||
85 | */ | ||
86 | case DT_MIPS_SYMTABNO: | ||
87 | symtabno = FUNC(swap_uint)(dyn->d_un.d_val); | ||
88 | break; | ||
89 | /* | ||
90 | * This member holds the index of the first dynamic | ||
91 | * symbol table entry that corresponds to an entry in | ||
92 | * the GOT. | ||
93 | */ | ||
94 | case DT_MIPS_GOTSYM: | ||
95 | gotsym = FUNC(swap_uint)(dyn->d_un.d_val); | ||
96 | break; | ||
97 | } | ||
98 | |||
99 | dyn++; | ||
100 | } | ||
101 | |||
102 | if (local_gotno > 2 || symtabno - gotsym) { | ||
103 | fprintf(stderr, | ||
104 | "%s: '%s' contains unexpected GOT entries\n", | ||
105 | program_name, path); | ||
106 | return false; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | return true; | ||
111 | } | ||
112 | |||
113 | static inline bool FUNC(get_symbols)(const char *path, void *vdso) | ||
114 | { | ||
115 | const ELF(Ehdr) *ehdr = vdso; | ||
116 | void *shdrs, *symtab; | ||
117 | ELF(Shdr) *shdr; | ||
118 | const ELF(Sym) *sym; | ||
119 | char *strtab, *name; | ||
120 | uint16_t sh_count, sh_entsize, st_count, st_entsize, i, j; | ||
121 | uint64_t offset; | ||
122 | uint32_t flags; | ||
123 | |||
124 | shdrs = vdso + FUNC(swap_uint)(ehdr->e_shoff); | ||
125 | sh_count = swap_uint16(ehdr->e_shnum); | ||
126 | sh_entsize = swap_uint16(ehdr->e_shentsize); | ||
127 | |||
128 | for (i = 0; i < sh_count; i++) { | ||
129 | shdr = shdrs + (i * sh_entsize); | ||
130 | |||
131 | if (swap_uint32(shdr->sh_type) == SHT_SYMTAB) | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | if (i == sh_count) { | ||
136 | fprintf(stderr, "%s: '%s' has no symbol table\n", program_name, | ||
137 | path); | ||
138 | return false; | ||
139 | } | ||
140 | |||
141 | /* Get flags */ | ||
142 | flags = swap_uint32(ehdr->e_flags); | ||
143 | if (elf_class == ELFCLASS64) | ||
144 | elf_abi = ABI_N64; | ||
145 | else if (flags & EF_MIPS_ABI2) | ||
146 | elf_abi = ABI_N32; | ||
147 | else | ||
148 | elf_abi = ABI_O32; | ||
149 | |||
150 | /* Get symbol table. */ | ||
151 | symtab = vdso + FUNC(swap_uint)(shdr->sh_offset); | ||
152 | st_entsize = FUNC(swap_uint)(shdr->sh_entsize); | ||
153 | st_count = FUNC(swap_uint)(shdr->sh_size) / st_entsize; | ||
154 | |||
155 | /* Get string table. */ | ||
156 | shdr = shdrs + (swap_uint32(shdr->sh_link) * sh_entsize); | ||
157 | strtab = vdso + FUNC(swap_uint)(shdr->sh_offset); | ||
158 | |||
159 | /* Write offsets for symbols needed by the kernel. */ | ||
160 | for (i = 0; vdso_symbols[i].name; i++) { | ||
161 | if (!(vdso_symbols[i].abis & elf_abi)) | ||
162 | continue; | ||
163 | |||
164 | for (j = 0; j < st_count; j++) { | ||
165 | sym = symtab + (j * st_entsize); | ||
166 | name = strtab + swap_uint32(sym->st_name); | ||
167 | |||
168 | if (!strcmp(name, vdso_symbols[i].name)) { | ||
169 | offset = FUNC(swap_uint)(sym->st_value); | ||
170 | |||
171 | fprintf(out_file, | ||
172 | "\t.%s = 0x%" PRIx64 ",\n", | ||
173 | vdso_symbols[i].offset_name, offset); | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | if (j == st_count) { | ||
179 | fprintf(stderr, | ||
180 | "%s: '%s' is missing required symbol '%s'\n", | ||
181 | program_name, path, vdso_symbols[i].name); | ||
182 | return false; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | return true; | ||
187 | } | ||
diff --git a/arch/mips/vdso/sigreturn.S b/arch/mips/vdso/sigreturn.S new file mode 100644 index 000000000000..715bf5993529 --- /dev/null +++ b/arch/mips/vdso/sigreturn.S | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include "vdso.h" | ||
12 | |||
13 | #include <uapi/asm/unistd.h> | ||
14 | |||
15 | #include <asm/regdef.h> | ||
16 | #include <asm/asm.h> | ||
17 | |||
18 | .section .text | ||
19 | .cfi_sections .debug_frame | ||
20 | |||
21 | LEAF(__vdso_rt_sigreturn) | ||
22 | .cfi_startproc | ||
23 | .frame sp, 0, ra | ||
24 | .mask 0x00000000, 0 | ||
25 | .fmask 0x00000000, 0 | ||
26 | .cfi_signal_frame | ||
27 | |||
28 | li v0, __NR_rt_sigreturn | ||
29 | syscall | ||
30 | |||
31 | .cfi_endproc | ||
32 | END(__vdso_rt_sigreturn) | ||
33 | |||
34 | #if _MIPS_SIM == _MIPS_SIM_ABI32 | ||
35 | |||
36 | LEAF(__vdso_sigreturn) | ||
37 | .cfi_startproc | ||
38 | .frame sp, 0, ra | ||
39 | .mask 0x00000000, 0 | ||
40 | .fmask 0x00000000, 0 | ||
41 | .cfi_signal_frame | ||
42 | |||
43 | li v0, __NR_sigreturn | ||
44 | syscall | ||
45 | |||
46 | .cfi_endproc | ||
47 | END(__vdso_sigreturn) | ||
48 | |||
49 | #endif | ||
diff --git a/arch/mips/vdso/vdso.h b/arch/mips/vdso/vdso.h new file mode 100644 index 000000000000..0bb6b1adc385 --- /dev/null +++ b/arch/mips/vdso/vdso.h | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <asm/sgidefs.h> | ||
12 | |||
13 | #if _MIPS_SIM != _MIPS_SIM_ABI64 && defined(CONFIG_64BIT) | ||
14 | |||
15 | /* Building 32-bit VDSO for the 64-bit kernel. Fake a 32-bit Kconfig. */ | ||
16 | #undef CONFIG_64BIT | ||
17 | #define CONFIG_32BIT 1 | ||
18 | #ifndef __ASSEMBLY__ | ||
19 | #include <asm-generic/atomic64.h> | ||
20 | #endif | ||
21 | #endif | ||
22 | |||
23 | #ifndef __ASSEMBLY__ | ||
24 | |||
25 | #include <asm/asm.h> | ||
26 | #include <asm/page.h> | ||
27 | #include <asm/vdso.h> | ||
28 | |||
29 | static inline unsigned long get_vdso_base(void) | ||
30 | { | ||
31 | unsigned long addr; | ||
32 | |||
33 | /* | ||
34 | * We can't use cpu_has_mips_r6 since it needs the cpu_data[] | ||
35 | * kernel symbol. | ||
36 | */ | ||
37 | #ifdef CONFIG_CPU_MIPSR6 | ||
38 | /* | ||
39 | * lapc <symbol> is an alias to addiupc reg, <symbol> - . | ||
40 | * | ||
41 | * We can't use addiupc because there is no label-label | ||
42 | * support for the addiupc reloc | ||
43 | */ | ||
44 | __asm__("lapc %0, _start \n" | ||
45 | : "=r" (addr) : :); | ||
46 | #else | ||
47 | /* | ||
48 | * Get the base load address of the VDSO. We have to avoid generating | ||
49 | * relocations and references to the GOT because ld.so does not peform | ||
50 | * relocations on the VDSO. We use the current offset from the VDSO base | ||
51 | * and perform a PC-relative branch which gives the absolute address in | ||
52 | * ra, and take the difference. The assembler chokes on | ||
53 | * "li %0, _start - .", so embed the offset as a word and branch over | ||
54 | * it. | ||
55 | * | ||
56 | */ | ||
57 | |||
58 | __asm__( | ||
59 | " .set push \n" | ||
60 | " .set noreorder \n" | ||
61 | " bal 1f \n" | ||
62 | " nop \n" | ||
63 | " .word _start - . \n" | ||
64 | "1: lw %0, 0($31) \n" | ||
65 | " " STR(PTR_ADDU) " %0, $31, %0 \n" | ||
66 | " .set pop \n" | ||
67 | : "=r" (addr) | ||
68 | : | ||
69 | : "$31"); | ||
70 | #endif /* CONFIG_CPU_MIPSR6 */ | ||
71 | |||
72 | return addr; | ||
73 | } | ||
74 | |||
75 | static inline const union mips_vdso_data *get_vdso_data(void) | ||
76 | { | ||
77 | return (const union mips_vdso_data *)(get_vdso_base() - PAGE_SIZE); | ||
78 | } | ||
79 | |||
80 | #endif /* __ASSEMBLY__ */ | ||
diff --git a/arch/mips/vdso/vdso.lds.S b/arch/mips/vdso/vdso.lds.S new file mode 100644 index 000000000000..21655b6fefc5 --- /dev/null +++ b/arch/mips/vdso/vdso.lds.S | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Imagination Technologies | ||
3 | * Author: Alex Smith <alex.smith@imgtec.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <asm/sgidefs.h> | ||
12 | |||
13 | #if _MIPS_SIM == _MIPS_SIM_ABI64 | ||
14 | OUTPUT_FORMAT("elf64-tradlittlemips", "elf64-tradbigmips", "elf64-tradlittlemips") | ||
15 | #elif _MIPS_SIM == _MIPS_SIM_NABI32 | ||
16 | OUTPUT_FORMAT("elf32-ntradlittlemips", "elf32-ntradbigmips", "elf32-ntradlittlemips") | ||
17 | #else | ||
18 | OUTPUT_FORMAT("elf32-tradlittlemips", "elf32-tradbigmips", "elf32-tradlittlemips") | ||
19 | #endif | ||
20 | |||
21 | OUTPUT_ARCH(mips) | ||
22 | |||
23 | SECTIONS | ||
24 | { | ||
25 | PROVIDE(_start = .); | ||
26 | . = SIZEOF_HEADERS; | ||
27 | |||
28 | /* | ||
29 | * In order to retain compatibility with older toolchains we provide the | ||
30 | * ABI flags section ourself. Newer assemblers will automatically | ||
31 | * generate .MIPS.abiflags sections so we discard such input sections, | ||
32 | * and then manually define our own section here. genvdso will patch | ||
33 | * this section to have the correct name/type. | ||
34 | */ | ||
35 | .mips_abiflags : { *(.mips_abiflags) } :text :abiflags | ||
36 | |||
37 | .reginfo : { *(.reginfo) } :text :reginfo | ||
38 | |||
39 | .hash : { *(.hash) } :text | ||
40 | .gnu.hash : { *(.gnu.hash) } | ||
41 | .dynsym : { *(.dynsym) } | ||
42 | .dynstr : { *(.dynstr) } | ||
43 | .gnu.version : { *(.gnu.version) } | ||
44 | .gnu.version_d : { *(.gnu.version_d) } | ||
45 | .gnu.version_r : { *(.gnu.version_r) } | ||
46 | |||
47 | .note : { *(.note.*) } :text :note | ||
48 | |||
49 | .text : { *(.text*) } :text | ||
50 | PROVIDE (__etext = .); | ||
51 | PROVIDE (_etext = .); | ||
52 | PROVIDE (etext = .); | ||
53 | |||
54 | .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr | ||
55 | .eh_frame : { KEEP (*(.eh_frame)) } :text | ||
56 | |||
57 | .dynamic : { *(.dynamic) } :text :dynamic | ||
58 | |||
59 | .rodata : { *(.rodata*) } :text | ||
60 | |||
61 | _end = .; | ||
62 | PROVIDE(end = .); | ||
63 | |||
64 | /DISCARD/ : { | ||
65 | *(.MIPS.abiflags) | ||
66 | *(.gnu.attributes) | ||
67 | *(.note.GNU-stack) | ||
68 | *(.data .data.* .gnu.linkonce.d.* .sdata*) | ||
69 | *(.bss .sbss .dynbss .dynsbss) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | PHDRS | ||
74 | { | ||
75 | /* | ||
76 | * Provide a PT_MIPS_ABIFLAGS header to assign the ABI flags section | ||
77 | * to. We can specify the header type directly here so no modification | ||
78 | * is needed later on. | ||
79 | */ | ||
80 | abiflags 0x70000003; | ||
81 | |||
82 | /* | ||
83 | * The ABI flags header must exist directly after the PT_INTERP header, | ||
84 | * so we must explicitly place the PT_MIPS_REGINFO header after it to | ||
85 | * stop the linker putting one in at the start. | ||
86 | */ | ||
87 | reginfo 0x70000000; | ||
88 | |||
89 | text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ | ||
90 | dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ | ||
91 | note PT_NOTE FLAGS(4); /* PF_R */ | ||
92 | eh_frame_hdr PT_GNU_EH_FRAME; | ||
93 | } | ||
94 | |||
95 | VERSION | ||
96 | { | ||
97 | LINUX_2.6 { | ||
98 | local: *; | ||
99 | }; | ||
100 | } | ||