]>
Commit | Line | Data |
---|---|---|
1 | /* Copyright (C) 1995-2021 Free Software Foundation, Inc. | |
2 | ||
3 | This file is part of the GNU C Library. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public License as | |
7 | published by the Free Software Foundation; either version 2.1 of the | |
8 | License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
17 | <https://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #ifndef dl_machine_h | |
20 | #define dl_machine_h | |
21 | ||
22 | #define ELF_MACHINE_NAME "microblaze" | |
23 | ||
24 | #include <sys/param.h> | |
25 | #include <tls.h> | |
26 | ||
27 | /* Return nonzero iff ELF header is compatible with the running host. */ | |
28 | static inline int | |
29 | elf_machine_matches_host (const Elf32_Ehdr *ehdr) | |
30 | { | |
31 | return (ehdr->e_machine == EM_MICROBLAZE); | |
32 | } | |
33 | ||
34 | /* Return the link-time address of _DYNAMIC. Conveniently, this is the | |
35 | first element of the GOT. This must be inlined in a function which | |
36 | uses global data. */ | |
37 | static inline Elf32_Addr | |
38 | elf_machine_dynamic (void) | |
39 | { | |
40 | /* This produces a GOTOFF reloc that resolves to zero at link time, so in | |
41 | fact just loads from the GOT register directly. By doing it without | |
42 | an asm we can let the compiler choose any register. */ | |
43 | ||
44 | Elf32_Addr got_entry_0; | |
45 | __asm__ __volatile__( | |
46 | "lwi %0,r20,0" | |
47 | :"=r"(got_entry_0) | |
48 | ); | |
49 | return got_entry_0; | |
50 | } | |
51 | ||
52 | /* Return the run-time load address of the shared object. */ | |
53 | static inline Elf32_Addr | |
54 | elf_machine_load_address (void) | |
55 | { | |
56 | /* Compute the difference between the runtime address of _DYNAMIC as seen | |
57 | by a GOTOFF reference, and the link-time address found in the special | |
58 | unrelocated first GOT entry. */ | |
59 | ||
60 | Elf32_Addr dyn; | |
61 | __asm__ __volatile__ ( | |
62 | "addik %0,r20,_DYNAMIC@GOTOFF" | |
63 | : "=r"(dyn) | |
64 | ); | |
65 | return dyn - elf_machine_dynamic (); | |
66 | } | |
67 | ||
68 | /* Set up the loaded object described by L so its unrelocated PLT | |
69 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
70 | ||
71 | static inline int __attribute__ ((always_inline)) | |
72 | elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) | |
73 | { | |
74 | extern void _dl_runtime_resolve (Elf32_Word); | |
75 | extern void _dl_runtime_profile (Elf32_Word); | |
76 | ||
77 | return lazy; | |
78 | } | |
79 | ||
80 | /* The PLT uses Elf32_Rela relocs. */ | |
81 | #define elf_machine_relplt elf_machine_rela | |
82 | ||
83 | /* Mask identifying addresses reserved for the user program, | |
84 | where the dynamic linker should not map anything. */ | |
85 | #define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL | |
86 | ||
87 | /* Initial entry point code for the dynamic linker. | |
88 | The C function `_dl_start' is the real entry point; | |
89 | its return value is the user program's entry point. */ | |
90 | ||
91 | #define RTLD_START asm ("\ | |
92 | .text\n\ | |
93 | .globl _start\n\ | |
94 | .type _start,@function\n\ | |
95 | _start:\n\ | |
96 | addk r5,r0,r1\n\ | |
97 | addk r3,r0,r0\n\ | |
98 | 1:\n\ | |
99 | addik r5,r5,4\n\ | |
100 | lw r4,r5,r0\n\ | |
101 | bneid r4,1b\n\ | |
102 | addik r3,r3,1\n\ | |
103 | addik r3,r3,-1\n\ | |
104 | addk r5,r0,r1\n\ | |
105 | sw r3,r5,r0\n\ | |
106 | addik r1,r1,-24\n\ | |
107 | sw r15,r1,r0\n\ | |
108 | brlid r15,_dl_start\n\ | |
109 | nop\n\ | |
110 | /* FALLTHRU. */\n\ | |
111 | \n\ | |
112 | .globl _dl_start_user\n\ | |
113 | .type _dl_start_user,@function\n\ | |
114 | _dl_start_user:\n\ | |
115 | mfs r20,rpc\n\ | |
116 | addik r20,r20,_GLOBAL_OFFSET_TABLE_+8\n\ | |
117 | lwi r4,r20,_dl_skip_args@GOTOFF\n\ | |
118 | lwi r5,r1,24\n\ | |
119 | rsubk r5,r4,r5\n\ | |
120 | addk r4,r4,r4\n\ | |
121 | addk r4,r4,r4\n\ | |
122 | addk r1,r1,r4\n\ | |
123 | swi r5,r1,24\n\ | |
124 | swi r3,r1,20\n\ | |
125 | addk r6,r5,r0\n\ | |
126 | addk r5,r5,r5\n\ | |
127 | addk r5,r5,r5\n\ | |
128 | addik r7,r1,28\n\ | |
129 | addk r8,r7,r5\n\ | |
130 | addik r8,r8,4\n\ | |
131 | lwi r5,r20,_rtld_local@GOTOFF\n\ | |
132 | brlid r15,_dl_init\n\ | |
133 | nop\n\ | |
134 | lwi r5,r1,24\n\ | |
135 | lwi r3,r1,20\n\ | |
136 | addk r4,r5,r5\n\ | |
137 | addk r4,r4,r4\n\ | |
138 | addik r6,r1,28\n\ | |
139 | addk r7,r6,r4\n\ | |
140 | addik r7,r7,4\n\ | |
141 | addik r15,r20,_dl_fini@GOTOFF\n\ | |
142 | addik r15,r15,-8\n\ | |
143 | brad r3\n\ | |
144 | addik r1,r1,24\n\ | |
145 | nop\n\ | |
146 | .size _dl_start_user, . - _dl_start_user\n\ | |
147 | .previous"); | |
148 | ||
149 | /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or | |
150 | TLS variable, so undefined references should not be allowed to | |
151 | define the value. | |
152 | ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one | |
153 | of the main executable's symbols, as for a COPY reloc. */ | |
154 | #ifndef RTLD_BOOTSTRAP | |
155 | # define elf_machine_type_class(type) \ | |
156 | (((type) == R_MICROBLAZE_JUMP_SLOT \ | |
157 | || (type) == R_MICROBLAZE_TLSDTPREL32 \ | |
158 | || (type) == R_MICROBLAZE_TLSDTPMOD32 \ | |
159 | || (type) == R_MICROBLAZE_TLSTPREL32) \ | |
160 | * ELF_RTYPE_CLASS_PLT \ | |
161 | | ((type) == R_MICROBLAZE_COPY) * ELF_RTYPE_CLASS_COPY) | |
162 | #else | |
163 | # define elf_machine_type_class(type) \ | |
164 | (((type) == R_MICROBLAZE_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT \ | |
165 | | ((type) == R_MICROBLAZE_COPY) * ELF_RTYPE_CLASS_COPY) | |
166 | #endif | |
167 | ||
168 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ | |
169 | #define ELF_MACHINE_JMP_SLOT R_MICROBLAZE_JUMP_SLOT | |
170 | ||
171 | /* The microblaze never uses Elf32_Rel relocations. */ | |
172 | #define ELF_MACHINE_NO_REL 1 | |
173 | #define ELF_MACHINE_NO_RELA 0 | |
174 | ||
175 | static inline Elf32_Addr | |
176 | elf_machine_fixup_plt (struct link_map *map, lookup_t t, | |
177 | const ElfW(Sym) *refsym, const ElfW(Sym) *sym, | |
178 | const Elf32_Rela *reloc, | |
179 | Elf32_Addr *reloc_addr, Elf32_Addr value) | |
180 | { | |
181 | return *reloc_addr = value; | |
182 | } | |
183 | ||
184 | /* Return the final value of a plt relocation. Ignore the addend. */ | |
185 | static inline Elf32_Addr | |
186 | elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc, | |
187 | Elf32_Addr value) | |
188 | { | |
189 | return value; | |
190 | } | |
191 | ||
192 | #endif /* !dl_machine_h. */ | |
193 | ||
194 | /* Names of the architecture-specific auditing callback functions. */ | |
195 | #define ARCH_LA_PLTENTER microblaze_gnu_pltenter | |
196 | #define ARCH_LA_PLTEXIT microblaze_gnu_pltexit | |
197 | ||
198 | #ifdef RESOLVE_MAP | |
199 | ||
200 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). | |
201 | MAP is the object containing the reloc. */ | |
202 | ||
203 | /* Macro to put 32-bit relocation value into 2 words. */ | |
204 | #define PUT_REL_64(rel_addr,val) \ | |
205 | do { \ | |
206 | ((unsigned short *)(rel_addr))[1] = (val) >> 16; \ | |
207 | ((unsigned short *)(rel_addr))[3] = (val) & 0xffff; \ | |
208 | } while (0) | |
209 | ||
210 | auto inline void __attribute__ ((always_inline)) | |
211 | elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, | |
212 | const Elf32_Sym *sym, const struct r_found_version *version, | |
213 | void *const reloc_addr_arg, int skip_ifunc) | |
214 | { | |
215 | Elf32_Addr *const reloc_addr = reloc_addr_arg; | |
216 | const int r_type = ELF32_R_TYPE (reloc->r_info); | |
217 | ||
218 | if (__builtin_expect (r_type == R_MICROBLAZE_64_PCREL, 0)) | |
219 | PUT_REL_64 (reloc_addr, map->l_addr + reloc->r_addend); | |
220 | else if (r_type == R_MICROBLAZE_REL) | |
221 | *reloc_addr = map->l_addr + reloc->r_addend; | |
222 | else | |
223 | { | |
224 | const Elf32_Sym *const refsym = sym; | |
225 | struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type); | |
226 | Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true); | |
227 | ||
228 | value += reloc->r_addend; | |
229 | if (r_type == R_MICROBLAZE_GLOB_DAT | |
230 | || r_type == R_MICROBLAZE_JUMP_SLOT | |
231 | || r_type == R_MICROBLAZE_32) | |
232 | { | |
233 | *reloc_addr = value; | |
234 | } | |
235 | else if (r_type == R_MICROBLAZE_COPY) | |
236 | { | |
237 | if (sym != NULL && (sym->st_size > refsym->st_size | |
238 | || (sym->st_size < refsym->st_size && GLRO (dl_verbose))) ) | |
239 | { | |
240 | const char *strtab; | |
241 | ||
242 | strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); | |
243 | _dl_error_printf ("\ | |
244 | %s: Symbol `%s' has different size in shared object, consider re-linking\n", | |
245 | RTLD_PROGNAME, strtab + refsym->st_name); | |
246 | } | |
247 | memcpy (reloc_addr_arg, (void *) value, | |
248 | MIN (sym->st_size, refsym->st_size)); | |
249 | } | |
250 | else if (r_type == R_MICROBLAZE_NONE) | |
251 | { | |
252 | } | |
253 | #if !defined RTLD_BOOTSTRAP | |
254 | else if (r_type == R_MICROBLAZE_TLSDTPMOD32) | |
255 | { | |
256 | if (sym_map != NULL) | |
257 | *reloc_addr = sym_map->l_tls_modid; | |
258 | } | |
259 | else if (r_type == R_MICROBLAZE_TLSDTPREL32) | |
260 | { | |
261 | if (sym != NULL) | |
262 | *reloc_addr = sym->st_value + reloc->r_addend; | |
263 | } | |
264 | else if (r_type == R_MICROBLAZE_TLSTPREL32) | |
265 | { | |
266 | if (sym != NULL) | |
267 | { | |
268 | CHECK_STATIC_TLS (map, sym_map); | |
269 | *reloc_addr = sym->st_value + sym_map->l_tls_offset + reloc->r_addend; | |
270 | } | |
271 | } | |
272 | #endif | |
273 | else | |
274 | { | |
275 | _dl_reloc_bad_type (map, r_type, 0); | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | auto inline void | |
281 | elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc, | |
282 | void *const reloc_addr_arg) | |
283 | { | |
284 | Elf32_Addr *const reloc_addr = reloc_addr_arg; | |
285 | PUT_REL_64 (reloc_addr, l_addr + reloc->r_addend); | |
286 | } | |
287 | ||
288 | auto inline void | |
289 | elf_machine_lazy_rel (struct link_map *map, | |
290 | Elf32_Addr l_addr, const Elf32_Rela *reloc, | |
291 | int skip_ifunc) | |
292 | { | |
293 | Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset); | |
294 | if (ELF32_R_TYPE (reloc->r_info) == R_MICROBLAZE_JUMP_SLOT) | |
295 | *reloc_addr += l_addr; | |
296 | else | |
297 | _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 1); | |
298 | } | |
299 | ||
300 | #endif /* RESOLVE_MAP. */ |