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 /arch/mips/vdso | |
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>
Diffstat (limited to 'arch/mips/vdso')
-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 |
8 files changed, 941 insertions, 0 deletions
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 | } | ||