hppa_reloc.c revision 1.51 1 1.51 riastrad /* $NetBSD: hppa_reloc.c,v 1.51 2024/07/22 23:10:46 riastradh Exp $ */
2 1.1 fredette
3 1.1 fredette /*-
4 1.21 skrll * Copyright (c) 2002, 2004 The NetBSD Foundation, Inc.
5 1.1 fredette * All rights reserved.
6 1.1 fredette *
7 1.1 fredette * This code is derived from software contributed to The NetBSD Foundation
8 1.21 skrll * by Matt Fredette and Nick Hudson.
9 1.1 fredette *
10 1.1 fredette * Redistribution and use in source and binary forms, with or without
11 1.1 fredette * modification, are permitted provided that the following conditions
12 1.1 fredette * are met:
13 1.1 fredette * 1. Redistributions of source code must retain the above copyright
14 1.1 fredette * notice, this list of conditions and the following disclaimer.
15 1.1 fredette * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 fredette * notice, this list of conditions and the following disclaimer in the
17 1.1 fredette * documentation and/or other materials provided with the distribution.
18 1.1 fredette *
19 1.1 fredette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 fredette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 fredette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 fredette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 fredette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 fredette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 fredette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 fredette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 fredette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 fredette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 fredette * POSSIBILITY OF SUCH DAMAGE.
30 1.1 fredette */
31 1.1 fredette
32 1.51 riastrad /*
33 1.51 riastrad * HP PA-RISC ELF relocations.
34 1.51 riastrad *
35 1.51 riastrad * References:
36 1.51 riastrad *
37 1.51 riastrad * [PAELF] Processor-Specific ELF Supplement for PA-RISC, Version
38 1.51 riastrad * 1.5, 1998-08-20.
39 1.51 riastrad * https://parisc.wiki.kernel.org/images-parisc/0/0e/Elf-pa-hp.pdf
40 1.51 riastrad * https://web.archive.org/web/20240712004045/https://parisc.wiki.kernel.org/images-parisc/0/0e/Elf-pa-hp.pdf
41 1.51 riastrad *
42 1.51 riastrad * [PATLS] Randolph Chung, Carlos O'Donell, and John David
43 1.51 riastrad * Anglin, `Implementing Thread Local Storage for HP PA-RISC
44 1.51 riastrad * Linux', 2013-11-11.
45 1.51 riastrad * http://www.parisc-linux.org/documentation/tls/hppa-tls-implementation.pdf
46 1.51 riastrad * https://web.archive.org/web/20240722131647/http://www.parisc-linux.org/documentation/tls/hppa-tls-implementation.pdf
47 1.51 riastrad */
48 1.51 riastrad
49 1.23 skrll #include <sys/cdefs.h>
50 1.23 skrll #ifndef lint
51 1.51 riastrad __RCSID("$NetBSD: hppa_reloc.c,v 1.51 2024/07/22 23:10:46 riastradh Exp $");
52 1.23 skrll #endif /* not lint */
53 1.23 skrll
54 1.1 fredette #include <stdlib.h>
55 1.1 fredette #include <sys/types.h>
56 1.1 fredette #include <sys/queue.h>
57 1.1 fredette
58 1.24 skrll #include <string.h>
59 1.24 skrll
60 1.1 fredette #include "rtld.h"
61 1.1 fredette #include "debug.h"
62 1.1 fredette
63 1.1 fredette #ifdef RTLD_DEBUG_HPPA
64 1.14 mycroft #define hdbg(x) xprintf x
65 1.1 fredette #else
66 1.1 fredette #define hdbg(x) /* nothing */
67 1.1 fredette #endif
68 1.12 mycroft
69 1.20 skrll caddr_t _rtld_bind(const Obj_Entry *, const Elf_Addr);
70 1.12 mycroft void _rtld_bind_start(void);
71 1.13 mycroft void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *);
72 1.49 skrll void _rtld_set_dp(Elf_Addr *);
73 1.1 fredette
74 1.1 fredette /*
75 1.24 skrll * It is possible for the compiler to emit relocations for unaligned data.
76 1.24 skrll * We handle this situation with these inlines.
77 1.24 skrll */
78 1.24 skrll #define RELOC_ALIGNED_P(x) \
79 1.24 skrll (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
80 1.24 skrll
81 1.24 skrll static inline Elf_Addr
82 1.24 skrll load_ptr(void *where)
83 1.24 skrll {
84 1.24 skrll if (__predict_true(RELOC_ALIGNED_P(where)))
85 1.24 skrll return *(Elf_Addr *)where;
86 1.24 skrll else {
87 1.24 skrll Elf_Addr res;
88 1.24 skrll
89 1.24 skrll (void)memcpy(&res, where, sizeof(res));
90 1.24 skrll return res;
91 1.24 skrll }
92 1.24 skrll }
93 1.24 skrll
94 1.24 skrll static inline void
95 1.24 skrll store_ptr(void *where, Elf_Addr val)
96 1.24 skrll {
97 1.24 skrll if (__predict_true(RELOC_ALIGNED_P(where)))
98 1.24 skrll *(Elf_Addr *)where = val;
99 1.24 skrll else
100 1.24 skrll (void)memcpy(where, &val, sizeof(val));
101 1.24 skrll }
102 1.24 skrll
103 1.42 skrll static __inline void
104 1.42 skrll fdc(void *addr)
105 1.42 skrll {
106 1.42 skrll __asm volatile("fdc %%r0(%%sr0, %0)" : : "r" (addr));
107 1.42 skrll }
108 1.42 skrll
109 1.42 skrll static __inline void
110 1.46 skrll fic(void *addr)
111 1.46 skrll {
112 1.42 skrll __asm volatile("fic %%r0(%%sr0,%0)" : : "r" (addr));
113 1.46 skrll }
114 1.42 skrll
115 1.42 skrll static __inline void
116 1.42 skrll sync(void)
117 1.42 skrll {
118 1.42 skrll __asm volatile("sync" : : : "memory");
119 1.42 skrll }
120 1.42 skrll
121 1.42 skrll #define PLT_STUB_MAGIC1 0x00c0ffee
122 1.42 skrll #define PLT_STUB_MAGIC2 0xdeadbeef
123 1.42 skrll
124 1.42 skrll #define PLT_STUB_INSN1 0x0e801081 /* ldw 0(%r20), %r1 */
125 1.42 skrll #define PLT_STUB_INSN2 0xe820c000 /* bv %r0(%r1) */
126 1.42 skrll
127 1.24 skrll /*
128 1.34 skrll * In the runtime architecture (ABI), PLABEL function pointers are
129 1.34 skrll * distinguished from normal function pointers by having the next-least-
130 1.34 skrll * significant bit set. (This bit is referred to as the L field in HP
131 1.34 skrll * documentation). The $$dyncall millicode is aware of this.
132 1.1 fredette */
133 1.1 fredette #define RTLD_MAKE_PLABEL(plabel) (((Elf_Addr)(plabel)) | (1 << 1))
134 1.1 fredette #define RTLD_IS_PLABEL(addr) (((Elf_Addr)(addr)) & (1 << 1))
135 1.1 fredette #define RTLD_GET_PLABEL(addr) ((hppa_plabel *) (((Elf_Addr)addr) & ~3))
136 1.1 fredette
137 1.1 fredette /*
138 1.1 fredette * This is the PLABEL structure. The function PC and
139 1.1 fredette * shared linkage members must come first, as they are
140 1.1 fredette * the actual PLABEL.
141 1.1 fredette */
142 1.1 fredette typedef struct _hppa_plabel {
143 1.1 fredette Elf_Addr hppa_plabel_pc;
144 1.1 fredette Elf_Addr hppa_plabel_sl;
145 1.1 fredette SLIST_ENTRY(_hppa_plabel) hppa_plabel_next;
146 1.1 fredette } hppa_plabel;
147 1.1 fredette
148 1.1 fredette /*
149 1.46 skrll * For now allocated PLABEL structures are tracked on a
150 1.1 fredette * singly linked list. This maybe should be revisited.
151 1.1 fredette */
152 1.1 fredette static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list
153 1.1 fredette = SLIST_HEAD_INITIALIZER(hppa_plabel_list);
154 1.1 fredette
155 1.1 fredette /*
156 1.1 fredette * Because I'm hesitant to use NEW while relocating self,
157 1.1 fredette * this is a small pool of preallocated PLABELs.
158 1.1 fredette */
159 1.40 chs #define HPPA_PLABEL_PRE (32)
160 1.1 fredette static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE];
161 1.1 fredette static int hppa_plabel_pre_next = 0;
162 1.1 fredette
163 1.20 skrll void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
164 1.20 skrll int _rtld_relocate_plt_objects(const Obj_Entry *);
165 1.25 skrll static inline int _rtld_relocate_plt_object(const Obj_Entry *,
166 1.25 skrll const Elf_Rela *, Elf_Addr *);
167 1.25 skrll
168 1.1 fredette /*
169 1.1 fredette * This bootstraps the dynamic linker by relocating its GOT.
170 1.1 fredette * On the hppa, unlike on other architectures, static strings
171 1.1 fredette * are found through the GOT. Static strings are essential
172 1.46 skrll * for RTLD_DEBUG, and I suspect they're used early even when
173 1.1 fredette * !defined(RTLD_DEBUG), making relocating the GOT essential.
174 1.1 fredette *
175 1.1 fredette * It gets worse. Relocating the GOT doesn't mean just walking
176 1.1 fredette * it and adding the relocbase to all of the entries. You must
177 1.46 skrll * find and use the GOT relocations, since those RELA relocations
178 1.46 skrll * have the necessary addends - the GOT comes initialized as
179 1.1 fredette * zeroes.
180 1.1 fredette */
181 1.1 fredette void
182 1.20 skrll _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
183 1.1 fredette {
184 1.1 fredette const Elf_Rela *relafirst, *rela, *relalim;
185 1.22 chs Elf_Addr relasz;
186 1.24 skrll void *where;
187 1.20 skrll Elf_Addr *pltgot;
188 1.20 skrll const Elf_Rela *plabel_relocs[HPPA_PLABEL_PRE];
189 1.20 skrll int nplabel_relocs = 0;
190 1.20 skrll int i;
191 1.20 skrll const Elf_Sym *symtab, *sym;
192 1.20 skrll unsigned long symnum;
193 1.20 skrll hppa_plabel *plabel;
194 1.1 fredette
195 1.22 chs /*
196 1.22 chs * Process the DYNAMIC section, looking for the non-PLT relocations.
197 1.46 skrll */
198 1.1 fredette relafirst = NULL;
199 1.22 chs relasz = 0;
200 1.22 chs symtab = NULL;
201 1.22 chs pltgot = NULL;
202 1.1 fredette for (; dynp->d_tag != DT_NULL; ++dynp) {
203 1.1 fredette switch (dynp->d_tag) {
204 1.1 fredette
205 1.1 fredette case DT_RELA:
206 1.1 fredette relafirst = (const Elf_Rela *)
207 1.1 fredette (relocbase + dynp->d_un.d_ptr);
208 1.1 fredette break;
209 1.1 fredette
210 1.1 fredette case DT_RELASZ:
211 1.1 fredette relasz = dynp->d_un.d_val;
212 1.1 fredette break;
213 1.20 skrll
214 1.20 skrll case DT_SYMTAB:
215 1.20 skrll symtab = (const Elf_Sym *)
216 1.20 skrll (relocbase + dynp->d_un.d_ptr);
217 1.20 skrll break;
218 1.20 skrll
219 1.20 skrll case DT_PLTGOT:
220 1.20 skrll pltgot = (Elf_Addr *)
221 1.20 skrll (relocbase + dynp->d_un.d_ptr);
222 1.35 skrll break;
223 1.1 fredette }
224 1.1 fredette }
225 1.28 mjf relalim = (const Elf_Rela *)((const char *)relafirst + relasz);
226 1.1 fredette
227 1.18 skrll for (rela = relafirst; rela < relalim; rela++) {
228 1.20 skrll symnum = ELF_R_SYM(rela->r_info);
229 1.24 skrll where = (void *)(relocbase + rela->r_offset);
230 1.20 skrll
231 1.20 skrll switch (ELF_R_TYPE(rela->r_info)) {
232 1.20 skrll case R_TYPE(DIR32):
233 1.20 skrll if (symnum == 0)
234 1.46 skrll store_ptr(where,
235 1.24 skrll relocbase + rela->r_addend);
236 1.20 skrll else {
237 1.20 skrll sym = symtab + symnum;
238 1.46 skrll store_ptr(where,
239 1.24 skrll relocbase + rela->r_addend + sym->st_value);
240 1.20 skrll }
241 1.20 skrll break;
242 1.20 skrll
243 1.20 skrll case R_TYPE(PLABEL32):
244 1.20 skrll /*
245 1.20 skrll * PLABEL32 relocation processing is done in two phases
246 1.20 skrll *
247 1.20 skrll * i) local function relocations (symbol number == 0)
248 1.20 skrll * can be resolved immediately.
249 1.20 skrll *
250 1.20 skrll * ii) external function relocations are deferred until
251 1.20 skrll * we finish all other relocations so that global
252 1.20 skrll * data isn't accessed until all other non-PLT
253 1.20 skrll * relocations have been done.
254 1.20 skrll */
255 1.20 skrll if (symnum == 0)
256 1.46 skrll *((Elf_Addr *)where) =
257 1.20 skrll relocbase + rela->r_addend;
258 1.20 skrll else
259 1.20 skrll plabel_relocs[nplabel_relocs++] = rela;
260 1.20 skrll break;
261 1.20 skrll
262 1.20 skrll default:
263 1.20 skrll break;
264 1.20 skrll }
265 1.1 fredette }
266 1.1 fredette
267 1.20 skrll assert(nplabel_relocs < HPPA_PLABEL_PRE);
268 1.20 skrll for (i = 0; i < nplabel_relocs; i++) {
269 1.20 skrll rela = plabel_relocs[i];
270 1.24 skrll where = (void *)(relocbase + rela->r_offset);
271 1.20 skrll sym = symtab + ELF_R_SYM(rela->r_info);
272 1.46 skrll
273 1.20 skrll plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
274 1.20 skrll
275 1.35 skrll plabel->hppa_plabel_pc = (Elf_Addr)
276 1.20 skrll (relocbase + sym->st_value + rela->r_addend);
277 1.35 skrll plabel->hppa_plabel_sl = (Elf_Addr)pltgot;
278 1.20 skrll
279 1.35 skrll SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
280 1.20 skrll *((Elf_Addr *)where) = (Elf_Addr)(RTLD_MAKE_PLABEL(plabel));
281 1.20 skrll }
282 1.46 skrll
283 1.1 fredette #if defined(RTLD_DEBUG_HPPA)
284 1.18 skrll for (rela = relafirst; rela < relalim; rela++) {
285 1.24 skrll where = (void *)(relocbase + rela->r_offset);
286 1.20 skrll
287 1.20 skrll switch (ELF_R_TYPE(rela->r_info)) {
288 1.20 skrll case R_TYPE(DIR32):
289 1.20 skrll hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n",
290 1.1 fredette (void *)rela->r_offset,
291 1.1 fredette (void *)where,
292 1.1 fredette (void *)rela->r_addend,
293 1.20 skrll (void *)*((Elf_Addr *)where) ));
294 1.20 skrll break;
295 1.20 skrll
296 1.20 skrll case R_TYPE(PLABEL32):
297 1.20 skrll symnum = ELF_R_SYM(rela->r_info);
298 1.20 skrll if (symnum == 0) {
299 1.20 skrll hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n",
300 1.20 skrll (void *)rela->r_offset,
301 1.20 skrll (void *)where,
302 1.20 skrll (void *)rela->r_addend,
303 1.20 skrll (void *)*((Elf_Addr *)where) ));
304 1.20 skrll } else {
305 1.20 skrll sym = symtab + symnum;
306 1.20 skrll
307 1.20 skrll hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n",
308 1.20 skrll (void *)rela->r_offset,
309 1.20 skrll (void *)where,
310 1.20 skrll symnum,
311 1.20 skrll (void *)sym->st_value,
312 1.20 skrll (void *)rela->r_addend,
313 1.20 skrll (void *)*((Elf_Addr *)where) ));
314 1.20 skrll }
315 1.20 skrll break;
316 1.20 skrll default:
317 1.20 skrll hdbg(("rela XXX reloc\n"));
318 1.20 skrll break;
319 1.20 skrll }
320 1.1 fredette }
321 1.1 fredette #endif /* RTLD_DEBUG_HPPA */
322 1.1 fredette }
323 1.1 fredette
324 1.1 fredette /*
325 1.46 skrll * This allocates a PLABEL. If called with a non-NULL def, the
326 1.1 fredette * plabel is for the function associated with that definition
327 1.46 skrll * in the defining object defobj, plus the given addend. If
328 1.1 fredette * called with a NULL def, the plabel is for the function at
329 1.1 fredette * the (unrelocated) address in addend in the object defobj.
330 1.1 fredette */
331 1.1 fredette Elf_Addr
332 1.1 fredette _rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def,
333 1.1 fredette Elf_Addr addend)
334 1.1 fredette {
335 1.1 fredette Elf_Addr func_pc, func_sl;
336 1.1 fredette hppa_plabel *plabel;
337 1.1 fredette
338 1.1 fredette if (def != NULL) {
339 1.46 skrll
340 1.1 fredette /*
341 1.1 fredette * We assume that symbols of type STT_NOTYPE
342 1.1 fredette * are undefined. Return NULL for these.
343 1.1 fredette */
344 1.1 fredette if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE)
345 1.1 fredette return (Elf_Addr)NULL;
346 1.1 fredette
347 1.1 fredette /* Otherwise assert that this symbol must be a function. */
348 1.1 fredette assert(ELF_ST_TYPE(def->st_info) == STT_FUNC);
349 1.1 fredette
350 1.46 skrll func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
351 1.1 fredette addend);
352 1.1 fredette } else
353 1.1 fredette func_pc = (Elf_Addr)(defobj->relocbase + addend);
354 1.1 fredette
355 1.1 fredette /*
356 1.1 fredette * Search the existing PLABELs for one matching
357 1.1 fredette * this function. If there is one, return it.
358 1.1 fredette */
359 1.20 skrll func_sl = (Elf_Addr)(defobj->pltgot);
360 1.1 fredette SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next)
361 1.1 fredette if (plabel->hppa_plabel_pc == func_pc &&
362 1.1 fredette plabel->hppa_plabel_sl == func_sl)
363 1.1 fredette return RTLD_MAKE_PLABEL(plabel);
364 1.1 fredette
365 1.1 fredette /*
366 1.1 fredette * Once we've used up the preallocated set, we start
367 1.1 fredette * using NEW to allocate plabels.
368 1.1 fredette */
369 1.1 fredette if (hppa_plabel_pre_next < HPPA_PLABEL_PRE)
370 1.1 fredette plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
371 1.1 fredette else {
372 1.1 fredette plabel = NEW(hppa_plabel);
373 1.1 fredette if (plabel == NULL)
374 1.1 fredette return (Elf_Addr)-1;
375 1.1 fredette }
376 1.1 fredette
377 1.1 fredette /* Fill the new entry and insert it on the list. */
378 1.1 fredette plabel->hppa_plabel_pc = func_pc;
379 1.1 fredette plabel->hppa_plabel_sl = func_sl;
380 1.1 fredette SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
381 1.1 fredette
382 1.1 fredette return RTLD_MAKE_PLABEL(plabel);
383 1.1 fredette }
384 1.1 fredette
385 1.1 fredette /*
386 1.1 fredette * If a pointer is a PLABEL, this unwraps it.
387 1.1 fredette */
388 1.1 fredette const void *
389 1.1 fredette _rtld_function_descriptor_function(const void *addr)
390 1.1 fredette {
391 1.46 skrll return (RTLD_IS_PLABEL(addr) ?
392 1.1 fredette (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc :
393 1.1 fredette addr);
394 1.1 fredette }
395 1.1 fredette
396 1.2 mycroft /* This sets up an object's GOT. */
397 1.2 mycroft void
398 1.2 mycroft _rtld_setup_pltgot(const Obj_Entry *obj)
399 1.2 mycroft {
400 1.42 skrll Elf_Word *got = obj->pltgot;
401 1.42 skrll
402 1.42 skrll assert(got[-2] == PLT_STUB_MAGIC1);
403 1.42 skrll assert(got[-1] == PLT_STUB_MAGIC2);
404 1.46 skrll
405 1.42 skrll __rtld_setup_hppa_pltgot(obj, got);
406 1.42 skrll
407 1.42 skrll fdc(&got[-2]);
408 1.42 skrll fdc(&got[-1]);
409 1.42 skrll fdc(&got[1]);
410 1.42 skrll sync();
411 1.42 skrll fic(&got[-2]);
412 1.42 skrll fic(&got[-1]);
413 1.42 skrll fic(&got[1]);
414 1.42 skrll sync();
415 1.42 skrll
416 1.42 skrll /*
417 1.42 skrll * libc makes use of %t1 (%r22) to pass errno values to __cerror. Fixup
418 1.42 skrll * the PLT stub to not use %r22.
419 1.42 skrll */
420 1.42 skrll got[-7] = PLT_STUB_INSN1;
421 1.42 skrll got[-6] = PLT_STUB_INSN2;
422 1.42 skrll fdc(&got[-7]);
423 1.42 skrll fdc(&got[-6]);
424 1.42 skrll sync();
425 1.42 skrll fic(&got[-7]);
426 1.42 skrll fic(&got[-6]);
427 1.42 skrll sync();
428 1.4 mycroft }
429 1.4 mycroft
430 1.4 mycroft int
431 1.33 joerg _rtld_relocate_nonplt_objects(Obj_Entry *obj)
432 1.4 mycroft {
433 1.5 mycroft const Elf_Rela *rela;
434 1.44 joerg const Elf_Sym *def = NULL;
435 1.44 joerg const Obj_Entry *defobj = NULL;
436 1.44 joerg unsigned long last_symnum = ULONG_MAX;
437 1.5 mycroft
438 1.49 skrll /*
439 1.49 skrll * This will be done by the crt0 code, but make sure it's set
440 1.49 skrll * early so that symbols overridden by the non-pic binary
441 1.49 skrll * get the right DP value.
442 1.49 skrll */
443 1.49 skrll if (obj->mainprog) {
444 1.49 skrll hdbg(("setting DP to %p", obj->pltgot));
445 1.49 skrll _rtld_set_dp(obj->pltgot);
446 1.49 skrll }
447 1.49 skrll
448 1.5 mycroft for (rela = obj->rela; rela < obj->relalim; rela++) {
449 1.5 mycroft Elf_Addr *where;
450 1.5 mycroft Elf_Addr tmp;
451 1.5 mycroft
452 1.5 mycroft where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
453 1.5 mycroft
454 1.47 skrll unsigned long symnum = ELF_R_SYM(rela->r_info);
455 1.44 joerg /* First, handle DIR32 and PLABEL32 without symbol. */
456 1.47 skrll if (symnum == 0) {
457 1.44 joerg switch (ELF_R_TYPE(rela->r_info)) {
458 1.44 joerg default:
459 1.44 joerg break;
460 1.44 joerg case R_TYPE(DIR32):
461 1.5 mycroft tmp = (Elf_Addr)(obj->relocbase +
462 1.5 mycroft rela->r_addend);
463 1.5 mycroft
464 1.24 skrll if (load_ptr(where) != tmp)
465 1.24 skrll store_ptr(where, tmp);
466 1.20 skrll rdbg(("DIR32 in %s --> %p", obj->path,
467 1.24 skrll (void *)load_ptr(where)));
468 1.44 joerg continue;
469 1.44 joerg case R_TYPE(PLABEL32):
470 1.5 mycroft /*
471 1.5 mycroft * This is a PLABEL for a static function, and
472 1.5 mycroft * the dynamic linker has both allocated a PLT
473 1.5 mycroft * entry for this function and told us where it
474 1.5 mycroft * is. We can safely use the PLT entry as the
475 1.5 mycroft * PLABEL because there should be no other
476 1.5 mycroft * PLABEL reloc referencing this function.
477 1.5 mycroft * This object should also have an IPLT
478 1.5 mycroft * relocation to initialize the PLT entry.
479 1.5 mycroft *
480 1.5 mycroft * The dynamic linker should also have ensured
481 1.5 mycroft * that the addend has the
482 1.5 mycroft * next-least-significant bit set; the
483 1.5 mycroft * $$dyncall millicode uses this to distinguish
484 1.5 mycroft * a PLABEL pointer from a plain function
485 1.5 mycroft * pointer.
486 1.5 mycroft */
487 1.19 skrll tmp = (Elf_Addr)
488 1.19 skrll (obj->relocbase + rela->r_addend);
489 1.5 mycroft
490 1.5 mycroft if (*where != tmp)
491 1.5 mycroft *where = tmp;
492 1.14 mycroft rdbg(("PLABEL32 in %s --> %p", obj->path,
493 1.14 mycroft (void *)*where));
494 1.44 joerg continue;
495 1.44 joerg }
496 1.44 joerg }
497 1.44 joerg
498 1.44 joerg switch (ELF_R_TYPE(rela->r_info)) {
499 1.44 joerg case R_TYPE(DIR32):
500 1.44 joerg case R_TYPE(PLABEL32):
501 1.44 joerg case R_TYPE(COPY):
502 1.44 joerg case R_TYPE(TLS_TPREL32):
503 1.44 joerg case R_TYPE(TLS_DTPMOD32):
504 1.44 joerg case R_TYPE(TLS_DTPOFF32):
505 1.44 joerg if (last_symnum != symnum) {
506 1.44 joerg last_symnum = symnum;
507 1.44 joerg if (ELF_R_TYPE(rela->r_info) == R_TYPE(DIR32)) {
508 1.44 joerg /*
509 1.44 joerg * DIR32 relocation against local
510 1.44 joerg * symbols are special...
511 1.44 joerg */
512 1.44 joerg def = obj->symtab + symnum;
513 1.44 joerg defobj = obj;
514 1.44 joerg if (def->st_name == 0)
515 1.44 joerg break;
516 1.44 joerg }
517 1.44 joerg def = _rtld_find_symdef(symnum, obj, &defobj,
518 1.44 joerg false);
519 1.44 joerg if (def == NULL)
520 1.44 joerg return -1;
521 1.5 mycroft }
522 1.5 mycroft break;
523 1.44 joerg default:
524 1.44 joerg break;
525 1.44 joerg }
526 1.44 joerg
527 1.44 joerg switch (ELF_R_TYPE(rela->r_info)) {
528 1.44 joerg case R_TYPE(NONE):
529 1.44 joerg break;
530 1.44 joerg
531 1.44 joerg case R_TYPE(DIR32):
532 1.44 joerg tmp = (Elf_Addr)(defobj->relocbase +
533 1.44 joerg def->st_value + rela->r_addend);
534 1.44 joerg
535 1.44 joerg if (load_ptr(where) != tmp)
536 1.44 joerg store_ptr(where, tmp);
537 1.44 joerg rdbg(("DIR32 %s in %s --> %p in %s",
538 1.44 joerg obj->strtab + obj->symtab[symnum].st_name,
539 1.44 joerg obj->path, (void *)load_ptr(where),
540 1.44 joerg defobj->path));
541 1.44 joerg break;
542 1.44 joerg
543 1.44 joerg case R_TYPE(PLABEL32):
544 1.44 joerg tmp = _rtld_function_descriptor_alloc(defobj,
545 1.44 joerg def, rela->r_addend);
546 1.44 joerg if (tmp == (Elf_Addr)-1)
547 1.44 joerg return -1;
548 1.44 joerg
549 1.44 joerg if (*where != tmp)
550 1.44 joerg *where = tmp;
551 1.44 joerg rdbg(("PLABEL32 %s in %s --> %p in %s",
552 1.44 joerg obj->strtab + obj->symtab[symnum].st_name,
553 1.44 joerg obj->path, (void *)*where, defobj->path));
554 1.44 joerg break;
555 1.4 mycroft
556 1.5 mycroft case R_TYPE(COPY):
557 1.4 mycroft /*
558 1.5 mycroft * These are deferred until all other relocations have
559 1.5 mycroft * been done. All we do here is make sure that the
560 1.5 mycroft * COPY relocation is not in a shared library. They
561 1.5 mycroft * are allowed only in executable files.
562 1.4 mycroft */
563 1.10 mycroft if (obj->isdynamic) {
564 1.5 mycroft _rtld_error(
565 1.5 mycroft "%s: Unexpected R_COPY relocation in shared library",
566 1.5 mycroft obj->path);
567 1.4 mycroft return -1;
568 1.5 mycroft }
569 1.14 mycroft rdbg(("COPY (avoid in main)"));
570 1.5 mycroft break;
571 1.4 mycroft
572 1.38 skrll case R_TYPE(TLS_TPREL32):
573 1.50 joerg if (!defobj->tls_static &&
574 1.50 joerg _rtld_tls_offset_allocate(__UNCONST(defobj)))
575 1.38 skrll return -1;
576 1.38 skrll
577 1.41 skrll *where = (Elf_Addr)(defobj->tlsoffset + def->st_value +
578 1.38 skrll rela->r_addend + sizeof(struct tls_tcb));
579 1.38 skrll
580 1.38 skrll rdbg(("TPREL32 %s in %s --> %p in %s",
581 1.38 skrll obj->strtab + obj->symtab[symnum].st_name,
582 1.38 skrll obj->path, (void *)*where, defobj->path));
583 1.38 skrll break;
584 1.38 skrll
585 1.36 skrll case R_TYPE(TLS_DTPMOD32):
586 1.36 skrll *where = (Elf_Addr)(defobj->tlsindex);
587 1.36 skrll
588 1.36 skrll rdbg(("TLS_DTPMOD32 %s in %s --> %p",
589 1.36 skrll obj->strtab + obj->symtab[symnum].st_name,
590 1.36 skrll obj->path, (void *)*where));
591 1.36 skrll
592 1.36 skrll break;
593 1.36 skrll
594 1.36 skrll case R_TYPE(TLS_DTPOFF32):
595 1.36 skrll *where = (Elf_Addr)(def->st_value);
596 1.36 skrll
597 1.36 skrll rdbg(("TLS_DTPOFF32 %s in %s --> %p",
598 1.36 skrll obj->strtab + obj->symtab[symnum].st_name,
599 1.36 skrll obj->path, (void *)*where));
600 1.36 skrll
601 1.36 skrll break;
602 1.36 skrll
603 1.5 mycroft default:
604 1.14 mycroft rdbg(("sym = %lu, type = %lu, offset = %p, "
605 1.5 mycroft "addend = %p, contents = %p, symbol = %s",
606 1.6 mycroft symnum, (u_long)ELF_R_TYPE(rela->r_info),
607 1.5 mycroft (void *)rela->r_offset, (void *)rela->r_addend,
608 1.24 skrll (void *)load_ptr(where),
609 1.6 mycroft obj->strtab + obj->symtab[symnum].st_name));
610 1.5 mycroft _rtld_error("%s: Unsupported relocation type %ld "
611 1.29 jmmv "in non-PLT relocations",
612 1.5 mycroft obj->path, (u_long) ELF_R_TYPE(rela->r_info));
613 1.4 mycroft return -1;
614 1.4 mycroft }
615 1.8 mycroft }
616 1.8 mycroft return 0;
617 1.8 mycroft }
618 1.8 mycroft
619 1.8 mycroft int
620 1.45 joerg _rtld_relocate_plt_lazy(Obj_Entry *obj)
621 1.8 mycroft {
622 1.8 mycroft const Elf_Rela *rela;
623 1.8 mycroft
624 1.8 mycroft for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
625 1.8 mycroft Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
626 1.8 mycroft Elf_Addr func_pc, func_sl;
627 1.8 mycroft
628 1.8 mycroft assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
629 1.8 mycroft
630 1.8 mycroft /*
631 1.8 mycroft * If this is an IPLT reloc for a static function,
632 1.8 mycroft * fully resolve the PLT entry now.
633 1.8 mycroft */
634 1.8 mycroft if (ELF_R_SYM(rela->r_info) == 0) {
635 1.8 mycroft func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
636 1.20 skrll func_sl = (Elf_Addr)(obj->pltgot);
637 1.8 mycroft }
638 1.8 mycroft
639 1.8 mycroft /*
640 1.8 mycroft * Otherwise set up for lazy binding.
641 1.8 mycroft */
642 1.8 mycroft else {
643 1.8 mycroft /*
644 1.8 mycroft * This function pointer points to the PLT
645 1.8 mycroft * stub added by the linker, and instead of
646 1.8 mycroft * a shared linkage value, we stash this
647 1.8 mycroft * relocation's offset. The PLT stub has
648 1.8 mycroft * already been set up to transfer to
649 1.8 mycroft * _rtld_bind_start.
650 1.8 mycroft */
651 1.20 skrll func_pc = ((Elf_Addr)(obj->pltgot)) - 16;
652 1.19 skrll func_sl = (Elf_Addr)
653 1.28 mjf ((const char *)rela - (const char *)(obj->pltrela));
654 1.8 mycroft }
655 1.20 skrll rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)",
656 1.20 skrll obj->path,
657 1.20 skrll (void *)where,
658 1.46 skrll (void *)where[0], (void *)where[1],
659 1.20 skrll (void *)func_pc, (void *)func_sl));
660 1.8 mycroft
661 1.8 mycroft /*
662 1.8 mycroft * Fill this PLT entry and return.
663 1.8 mycroft */
664 1.8 mycroft where[0] = func_pc;
665 1.8 mycroft where[1] = func_sl;
666 1.4 mycroft }
667 1.4 mycroft return 0;
668 1.1 fredette }
669 1.20 skrll
670 1.25 skrll static inline int
671 1.34 skrll _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela,
672 1.34 skrll Elf_Addr *tp)
673 1.20 skrll {
674 1.20 skrll Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
675 1.20 skrll const Elf_Sym *def;
676 1.20 skrll const Obj_Entry *defobj;
677 1.20 skrll Elf_Addr func_pc, func_sl;
678 1.31 christos unsigned long info = rela->r_info;
679 1.20 skrll
680 1.31 christos assert(ELF_R_TYPE(info) == R_TYPE(IPLT));
681 1.20 skrll
682 1.31 christos if (ELF_R_SYM(info) == 0) {
683 1.25 skrll func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
684 1.25 skrll func_sl = (Elf_Addr)(obj->pltgot);
685 1.25 skrll } else {
686 1.31 christos def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj,
687 1.31 christos tp != NULL);
688 1.31 christos if (__predict_false(def == NULL))
689 1.25 skrll return -1;
690 1.31 christos if (__predict_false(def == &_rtld_sym_zero))
691 1.31 christos return 0;
692 1.20 skrll
693 1.43 joerg if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
694 1.43 joerg if (tp == NULL)
695 1.43 joerg return 0;
696 1.43 joerg Elf_Addr ptr = _rtld_resolve_ifunc(defobj, def);
697 1.43 joerg assert(RTLD_IS_PLABEL(ptr));
698 1.43 joerg hppa_plabel *label = RTLD_GET_PLABEL(ptr);
699 1.43 joerg func_pc = label->hppa_plabel_pc;
700 1.43 joerg func_sl = label->hppa_plabel_sl;
701 1.43 joerg } else {
702 1.43 joerg func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
703 1.43 joerg rela->r_addend);
704 1.43 joerg func_sl = (Elf_Addr)(defobj->pltgot);
705 1.43 joerg }
706 1.20 skrll
707 1.25 skrll rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
708 1.25 skrll defobj->strtab + def->st_name,
709 1.46 skrll (void *)where[0], (void *)where[1],
710 1.25 skrll (void *)func_pc, (void *)func_sl));
711 1.25 skrll }
712 1.20 skrll /*
713 1.20 skrll * Fill this PLT entry and return.
714 1.20 skrll */
715 1.20 skrll if (where[0] != func_pc)
716 1.20 skrll where[0] = func_pc;
717 1.20 skrll if (where[1] != func_sl)
718 1.20 skrll where[1] = func_sl;
719 1.20 skrll
720 1.25 skrll if (tp)
721 1.25 skrll *tp = (Elf_Addr)where;
722 1.25 skrll
723 1.25 skrll return 0;
724 1.25 skrll }
725 1.25 skrll
726 1.25 skrll caddr_t
727 1.25 skrll _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
728 1.25 skrll {
729 1.28 mjf const Elf_Rela *rela;
730 1.32 skrll Elf_Addr new_value = 0; /* XXX gcc */
731 1.25 skrll int err;
732 1.25 skrll
733 1.28 mjf rela = (const Elf_Rela *)((const char *)obj->pltrela + reloff);
734 1.46 skrll
735 1.25 skrll assert(ELF_R_SYM(rela->r_info) != 0);
736 1.25 skrll
737 1.39 joerg _rtld_shared_enter();
738 1.46 skrll err = _rtld_relocate_plt_object(obj, rela, &new_value);
739 1.31 christos if (err)
740 1.25 skrll _rtld_die();
741 1.39 joerg _rtld_shared_exit();
742 1.25 skrll
743 1.25 skrll return (caddr_t)new_value;
744 1.20 skrll }
745 1.20 skrll
746 1.20 skrll int
747 1.20 skrll _rtld_relocate_plt_objects(const Obj_Entry *obj)
748 1.20 skrll {
749 1.25 skrll const Elf_Rela *rela = obj->pltrela;
750 1.46 skrll
751 1.25 skrll for (; rela < obj->pltrelalim; rela++) {
752 1.25 skrll if (_rtld_relocate_plt_object(obj, rela, NULL) < 0)
753 1.25 skrll return -1;
754 1.20 skrll }
755 1.20 skrll return 0;
756 1.20 skrll }
757 1.43 joerg
758 1.43 joerg Elf_Addr
759 1.43 joerg _rtld_call_function_addr(const Obj_Entry *obj, Elf_Addr ptr)
760 1.43 joerg {
761 1.43 joerg volatile hppa_plabel plabel;
762 1.43 joerg Elf_Addr (*f)(void);
763 1.43 joerg
764 1.43 joerg plabel.hppa_plabel_pc = (Elf_Addr)ptr;
765 1.43 joerg plabel.hppa_plabel_sl = (Elf_Addr)(obj->pltgot);
766 1.43 joerg f = (Elf_Addr (*)(void))RTLD_MAKE_PLABEL(&plabel);
767 1.43 joerg
768 1.43 joerg return f();
769 1.43 joerg }
770