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/genvdso.h | |
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/genvdso.h')
-rw-r--r-- | arch/mips/vdso/genvdso.h | 187 |
1 files changed, 187 insertions, 0 deletions
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 | } | ||