1 /* $NetBSD: hppa_reloc.c,v 1.43 2014/08/25 20:40:52 joerg Exp $ */
4 * Copyright (c) 2002, 2004 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette and Nick Hudson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: hppa_reloc.c,v 1.43 2014/08/25 20:40:52 joerg Exp $");
38 #include <sys/types.h>
39 #include <sys/queue.h>
46 #ifdef RTLD_DEBUG_HPPA
47 #define hdbg(x) xprintf x
49 #define hdbg(x) /* nothing */
52 caddr_t
_rtld_bind(const Obj_Entry
*, const Elf_Addr
);
53 void _rtld_bind_start(void);
54 void __rtld_setup_hppa_pltgot(const Obj_Entry
*, Elf_Addr
*);
57 * It is possible for the compiler to emit relocations for unaligned data.
58 * We handle this situation with these inlines.
60 #define RELOC_ALIGNED_P(x) \
61 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
63 static inline Elf_Addr
66 if (__predict_true(RELOC_ALIGNED_P(where
)))
67 return *(Elf_Addr
*)where
;
71 (void)memcpy(&res
, where
, sizeof(res
));
77 store_ptr(void *where
, Elf_Addr val
)
79 if (__predict_true(RELOC_ALIGNED_P(where
)))
80 *(Elf_Addr
*)where
= val
;
82 (void)memcpy(where
, &val
, sizeof(val
));
88 __asm
volatile("fdc %%r0(%%sr0, %0)" : : "r" (addr
));
94 __asm
volatile("fic %%r0(%%sr0,%0)" : : "r" (addr
));
100 __asm
volatile("sync" : : : "memory");
103 #define PLT_STUB_MAGIC1 0x00c0ffee
104 #define PLT_STUB_MAGIC2 0xdeadbeef
106 #define PLT_STUB_INSN1 0x0e801081 /* ldw 0(%r20), %r1 */
107 #define PLT_STUB_INSN2 0xe820c000 /* bv %r0(%r1) */
110 * In the runtime architecture (ABI), PLABEL function pointers are
111 * distinguished from normal function pointers by having the next-least-
112 * significant bit set. (This bit is referred to as the L field in HP
113 * documentation). The $$dyncall millicode is aware of this.
115 #define RTLD_MAKE_PLABEL(plabel) (((Elf_Addr)(plabel)) | (1 << 1))
116 #define RTLD_IS_PLABEL(addr) (((Elf_Addr)(addr)) & (1 << 1))
117 #define RTLD_GET_PLABEL(addr) ((hppa_plabel *) (((Elf_Addr)addr) & ~3))
120 * This is the PLABEL structure. The function PC and
121 * shared linkage members must come first, as they are
124 typedef struct _hppa_plabel
{
125 Elf_Addr hppa_plabel_pc
;
126 Elf_Addr hppa_plabel_sl
;
127 SLIST_ENTRY(_hppa_plabel
) hppa_plabel_next
;
131 * For now allocated PLABEL structures are tracked on a
132 * singly linked list. This maybe should be revisited.
134 static SLIST_HEAD(hppa_plabel_head
, _hppa_plabel
) hppa_plabel_list
135 = SLIST_HEAD_INITIALIZER(hppa_plabel_list
);
138 * Because I'm hesitant to use NEW while relocating self,
139 * this is a small pool of preallocated PLABELs.
141 #define HPPA_PLABEL_PRE (32)
142 static hppa_plabel hppa_plabel_pre
[HPPA_PLABEL_PRE
];
143 static int hppa_plabel_pre_next
= 0;
145 void _rtld_relocate_nonplt_self(Elf_Dyn
*, Elf_Addr
);
146 int _rtld_relocate_plt_objects(const Obj_Entry
*);
147 static inline int _rtld_relocate_plt_object(const Obj_Entry
*,
148 const Elf_Rela
*, Elf_Addr
*);
151 * This bootstraps the dynamic linker by relocating its GOT.
152 * On the hppa, unlike on other architectures, static strings
153 * are found through the GOT. Static strings are essential
154 * for RTLD_DEBUG, and I suspect they're used early even when
155 * !defined(RTLD_DEBUG), making relocating the GOT essential.
157 * It gets worse. Relocating the GOT doesn't mean just walking
158 * it and adding the relocbase to all of the entries. You must
159 * find and use the GOT relocations, since those RELA relocations
160 * have the necessary addends - the GOT comes initialized as
164 _rtld_relocate_nonplt_self(Elf_Dyn
*dynp
, Elf_Addr relocbase
)
166 const Elf_Rela
*relafirst
, *rela
, *relalim
;
170 const Elf_Rela
*plabel_relocs
[HPPA_PLABEL_PRE
];
171 int nplabel_relocs
= 0;
173 const Elf_Sym
*symtab
, *sym
;
174 unsigned long symnum
;
178 * Process the DYNAMIC section, looking for the non-PLT relocations.
184 for (; dynp
->d_tag
!= DT_NULL
; ++dynp
) {
185 switch (dynp
->d_tag
) {
188 relafirst
= (const Elf_Rela
*)
189 (relocbase
+ dynp
->d_un
.d_ptr
);
193 relasz
= dynp
->d_un
.d_val
;
197 symtab
= (const Elf_Sym
*)
198 (relocbase
+ dynp
->d_un
.d_ptr
);
202 pltgot
= (Elf_Addr
*)
203 (relocbase
+ dynp
->d_un
.d_ptr
);
207 relalim
= (const Elf_Rela
*)((const char *)relafirst
+ relasz
);
209 for (rela
= relafirst
; rela
< relalim
; rela
++) {
210 symnum
= ELF_R_SYM(rela
->r_info
);
211 where
= (void *)(relocbase
+ rela
->r_offset
);
213 switch (ELF_R_TYPE(rela
->r_info
)) {
217 relocbase
+ rela
->r_addend
);
219 sym
= symtab
+ symnum
;
221 relocbase
+ rela
->r_addend
+ sym
->st_value
);
225 case R_TYPE(PLABEL32
):
227 * PLABEL32 relocation processing is done in two phases
229 * i) local function relocations (symbol number == 0)
230 * can be resolved immediately.
232 * ii) external function relocations are deferred until
233 * we finish all other relocations so that global
234 * data isn't accessed until all other non-PLT
235 * relocations have been done.
238 *((Elf_Addr
*)where
) =
239 relocbase
+ rela
->r_addend
;
241 plabel_relocs
[nplabel_relocs
++] = rela
;
249 assert(nplabel_relocs
< HPPA_PLABEL_PRE
);
250 for (i
= 0; i
< nplabel_relocs
; i
++) {
251 rela
= plabel_relocs
[i
];
252 where
= (void *)(relocbase
+ rela
->r_offset
);
253 sym
= symtab
+ ELF_R_SYM(rela
->r_info
);
255 plabel
= &hppa_plabel_pre
[hppa_plabel_pre_next
++];
257 plabel
->hppa_plabel_pc
= (Elf_Addr
)
258 (relocbase
+ sym
->st_value
+ rela
->r_addend
);
259 plabel
->hppa_plabel_sl
= (Elf_Addr
)pltgot
;
261 SLIST_INSERT_HEAD(&hppa_plabel_list
, plabel
, hppa_plabel_next
);
262 *((Elf_Addr
*)where
) = (Elf_Addr
)(RTLD_MAKE_PLABEL(plabel
));
265 #if defined(RTLD_DEBUG_HPPA)
266 for (rela
= relafirst
; rela
< relalim
; rela
++) {
267 where
= (void *)(relocbase
+ rela
->r_offset
);
269 switch (ELF_R_TYPE(rela
->r_info
)) {
271 hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n",
272 (void *)rela
->r_offset
,
274 (void *)rela
->r_addend
,
275 (void *)*((Elf_Addr
*)where
) ));
278 case R_TYPE(PLABEL32
):
279 symnum
= ELF_R_SYM(rela
->r_info
);
281 hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n",
282 (void *)rela
->r_offset
,
284 (void *)rela
->r_addend
,
285 (void *)*((Elf_Addr
*)where
) ));
287 sym
= symtab
+ symnum
;
289 hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n",
290 (void *)rela
->r_offset
,
293 (void *)sym
->st_value
,
294 (void *)rela
->r_addend
,
295 (void *)*((Elf_Addr
*)where
) ));
299 hdbg(("rela XXX reloc\n"));
303 #endif /* RTLD_DEBUG_HPPA */
307 * This allocates a PLABEL. If called with a non-NULL def, the
308 * plabel is for the function associated with that definition
309 * in the defining object defobj, plus the given addend. If
310 * called with a NULL def, the plabel is for the function at
311 * the (unrelocated) address in addend in the object defobj.
314 _rtld_function_descriptor_alloc(const Obj_Entry
*defobj
, const Elf_Sym
*def
,
317 Elf_Addr func_pc
, func_sl
;
323 * We assume that symbols of type STT_NOTYPE
324 * are undefined. Return NULL for these.
326 if (ELF_ST_TYPE(def
->st_info
) == STT_NOTYPE
)
327 return (Elf_Addr
)NULL
;
329 /* Otherwise assert that this symbol must be a function. */
330 assert(ELF_ST_TYPE(def
->st_info
) == STT_FUNC
);
332 func_pc
= (Elf_Addr
)(defobj
->relocbase
+ def
->st_value
+
335 func_pc
= (Elf_Addr
)(defobj
->relocbase
+ addend
);
338 * Search the existing PLABELs for one matching
339 * this function. If there is one, return it.
341 func_sl
= (Elf_Addr
)(defobj
->pltgot
);
342 SLIST_FOREACH(plabel
, &hppa_plabel_list
, hppa_plabel_next
)
343 if (plabel
->hppa_plabel_pc
== func_pc
&&
344 plabel
->hppa_plabel_sl
== func_sl
)
345 return RTLD_MAKE_PLABEL(plabel
);
348 * Once we've used up the preallocated set, we start
349 * using NEW to allocate plabels.
351 if (hppa_plabel_pre_next
< HPPA_PLABEL_PRE
)
352 plabel
= &hppa_plabel_pre
[hppa_plabel_pre_next
++];
354 plabel
= NEW(hppa_plabel
);
359 /* Fill the new entry and insert it on the list. */
360 plabel
->hppa_plabel_pc
= func_pc
;
361 plabel
->hppa_plabel_sl
= func_sl
;
362 SLIST_INSERT_HEAD(&hppa_plabel_list
, plabel
, hppa_plabel_next
);
364 return RTLD_MAKE_PLABEL(plabel
);
368 * If a pointer is a PLABEL, this unwraps it.
371 _rtld_function_descriptor_function(const void *addr
)
373 return (RTLD_IS_PLABEL(addr
) ?
374 (const void *) RTLD_GET_PLABEL(addr
)->hppa_plabel_pc
:
378 /* This sets up an object's GOT. */
380 _rtld_setup_pltgot(const Obj_Entry
*obj
)
382 Elf_Word
*got
= obj
->pltgot
;
384 assert(got
[-2] == PLT_STUB_MAGIC1
);
385 assert(got
[-1] == PLT_STUB_MAGIC2
);
387 __rtld_setup_hppa_pltgot(obj
, got
);
399 * libc makes use of %t1 (%r22) to pass errno values to __cerror. Fixup
400 * the PLT stub to not use %r22.
402 got
[-7] = PLT_STUB_INSN1
;
403 got
[-6] = PLT_STUB_INSN2
;
413 _rtld_relocate_nonplt_objects(Obj_Entry
*obj
)
415 const Elf_Rela
*rela
;
417 for (rela
= obj
->rela
; rela
< obj
->relalim
; rela
++) {
420 const Obj_Entry
*defobj
;
422 unsigned long symnum
;
424 where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
425 symnum
= ELF_R_SYM(rela
->r_info
);
427 switch (ELF_R_TYPE(rela
->r_info
)) {
434 * This is either a DIR32 against a symbol
435 * (def->st_name != 0), or against a local
436 * section (def->st_name == 0).
438 def
= obj
->symtab
+ symnum
;
440 if (def
->st_name
!= 0)
441 def
= _rtld_find_symdef(symnum
, obj
,
446 tmp
= (Elf_Addr
)(defobj
->relocbase
+
447 def
->st_value
+ rela
->r_addend
);
449 if (load_ptr(where
) != tmp
)
450 store_ptr(where
, tmp
);
451 rdbg(("DIR32 %s in %s --> %p in %s",
452 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
453 obj
->path
, (void *)load_ptr(where
),
456 tmp
= (Elf_Addr
)(obj
->relocbase
+
459 if (load_ptr(where
) != tmp
)
460 store_ptr(where
, tmp
);
461 rdbg(("DIR32 in %s --> %p", obj
->path
,
462 (void *)load_ptr(where
)));
466 case R_TYPE(PLABEL32
):
468 def
= _rtld_find_symdef(symnum
, obj
, &defobj
,
473 tmp
= _rtld_function_descriptor_alloc(defobj
,
474 def
, rela
->r_addend
);
475 if (tmp
== (Elf_Addr
)-1)
480 rdbg(("PLABEL32 %s in %s --> %p in %s",
481 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
482 obj
->path
, (void *)*where
, defobj
->path
));
485 * This is a PLABEL for a static function, and
486 * the dynamic linker has both allocated a PLT
487 * entry for this function and told us where it
488 * is. We can safely use the PLT entry as the
489 * PLABEL because there should be no other
490 * PLABEL reloc referencing this function.
491 * This object should also have an IPLT
492 * relocation to initialize the PLT entry.
494 * The dynamic linker should also have ensured
495 * that the addend has the
496 * next-least-significant bit set; the
497 * $$dyncall millicode uses this to distinguish
498 * a PLABEL pointer from a plain function
502 (obj
->relocbase
+ rela
->r_addend
);
506 rdbg(("PLABEL32 in %s --> %p", obj
->path
,
513 * These are deferred until all other relocations have
514 * been done. All we do here is make sure that the
515 * COPY relocation is not in a shared library. They
516 * are allowed only in executable files.
518 if (obj
->isdynamic
) {
520 "%s: Unexpected R_COPY relocation in shared library",
524 rdbg(("COPY (avoid in main)"));
527 case R_TYPE(TLS_TPREL32
):
528 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
532 if (!defobj
->tls_done
&& _rtld_tls_offset_allocate(obj
))
535 *where
= (Elf_Addr
)(defobj
->tlsoffset
+ def
->st_value
+
536 rela
->r_addend
+ sizeof(struct tls_tcb
));
538 rdbg(("TPREL32 %s in %s --> %p in %s",
539 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
540 obj
->path
, (void *)*where
, defobj
->path
));
543 case R_TYPE(TLS_DTPMOD32
):
544 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
548 *where
= (Elf_Addr
)(defobj
->tlsindex
);
550 rdbg(("TLS_DTPMOD32 %s in %s --> %p",
551 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
552 obj
->path
, (void *)*where
));
556 case R_TYPE(TLS_DTPOFF32
):
557 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
561 *where
= (Elf_Addr
)(def
->st_value
);
563 rdbg(("TLS_DTPOFF32 %s in %s --> %p",
564 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
565 obj
->path
, (void *)*where
));
570 rdbg(("sym = %lu, type = %lu, offset = %p, "
571 "addend = %p, contents = %p, symbol = %s",
572 symnum
, (u_long
)ELF_R_TYPE(rela
->r_info
),
573 (void *)rela
->r_offset
, (void *)rela
->r_addend
,
574 (void *)load_ptr(where
),
575 obj
->strtab
+ obj
->symtab
[symnum
].st_name
));
576 _rtld_error("%s: Unsupported relocation type %ld "
577 "in non-PLT relocations",
578 obj
->path
, (u_long
) ELF_R_TYPE(rela
->r_info
));
586 _rtld_relocate_plt_lazy(const Obj_Entry
*obj
)
588 const Elf_Rela
*rela
;
590 for (rela
= obj
->pltrela
; rela
< obj
->pltrelalim
; rela
++) {
591 Elf_Addr
*where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
592 Elf_Addr func_pc
, func_sl
;
594 assert(ELF_R_TYPE(rela
->r_info
) == R_TYPE(IPLT
));
597 * If this is an IPLT reloc for a static function,
598 * fully resolve the PLT entry now.
600 if (ELF_R_SYM(rela
->r_info
) == 0) {
601 func_pc
= (Elf_Addr
)(obj
->relocbase
+ rela
->r_addend
);
602 func_sl
= (Elf_Addr
)(obj
->pltgot
);
606 * Otherwise set up for lazy binding.
610 * This function pointer points to the PLT
611 * stub added by the linker, and instead of
612 * a shared linkage value, we stash this
613 * relocation's offset. The PLT stub has
614 * already been set up to transfer to
617 func_pc
= ((Elf_Addr
)(obj
->pltgot
)) - 16;
619 ((const char *)rela
- (const char *)(obj
->pltrela
));
621 rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)",
624 (void *)where
[0], (void *)where
[1],
625 (void *)func_pc
, (void *)func_sl
));
628 * Fill this PLT entry and return.
637 _rtld_relocate_plt_object(const Obj_Entry
*obj
, const Elf_Rela
*rela
,
640 Elf_Word
*where
= (Elf_Word
*)(obj
->relocbase
+ rela
->r_offset
);
642 const Obj_Entry
*defobj
;
643 Elf_Addr func_pc
, func_sl
;
644 unsigned long info
= rela
->r_info
;
646 assert(ELF_R_TYPE(info
) == R_TYPE(IPLT
));
648 if (ELF_R_SYM(info
) == 0) {
649 func_pc
= (Elf_Addr
)(obj
->relocbase
+ rela
->r_addend
);
650 func_sl
= (Elf_Addr
)(obj
->pltgot
);
652 def
= _rtld_find_plt_symdef(ELF_R_SYM(info
), obj
, &defobj
,
654 if (__predict_false(def
== NULL
))
656 if (__predict_false(def
== &_rtld_sym_zero
))
659 if (ELF_ST_TYPE(def
->st_info
) == STT_GNU_IFUNC
) {
662 Elf_Addr ptr
= _rtld_resolve_ifunc(defobj
, def
);
663 assert(RTLD_IS_PLABEL(ptr
));
664 hppa_plabel
*label
= RTLD_GET_PLABEL(ptr
);
665 func_pc
= label
->hppa_plabel_pc
;
666 func_sl
= label
->hppa_plabel_sl
;
668 func_pc
= (Elf_Addr
)(defobj
->relocbase
+ def
->st_value
+
670 func_sl
= (Elf_Addr
)(defobj
->pltgot
);
673 rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
674 defobj
->strtab
+ def
->st_name
,
675 (void *)where
[0], (void *)where
[1],
676 (void *)func_pc
, (void *)func_sl
));
679 * Fill this PLT entry and return.
681 if (where
[0] != func_pc
)
683 if (where
[1] != func_sl
)
687 *tp
= (Elf_Addr
)where
;
693 _rtld_bind(const Obj_Entry
*obj
, Elf_Word reloff
)
695 const Elf_Rela
*rela
;
696 Elf_Addr new_value
= 0; /* XXX gcc */
699 rela
= (const Elf_Rela
*)((const char *)obj
->pltrela
+ reloff
);
701 assert(ELF_R_SYM(rela
->r_info
) != 0);
703 _rtld_shared_enter();
704 err
= _rtld_relocate_plt_object(obj
, rela
, &new_value
);
709 return (caddr_t
)new_value
;
713 _rtld_relocate_plt_objects(const Obj_Entry
*obj
)
715 const Elf_Rela
*rela
= obj
->pltrela
;
717 for (; rela
< obj
->pltrelalim
; rela
++) {
718 if (_rtld_relocate_plt_object(obj
, rela
, NULL
) < 0)
725 _rtld_call_function_void(const Obj_Entry
*obj
, Elf_Addr ptr
)
727 volatile hppa_plabel plabel
;
730 plabel
.hppa_plabel_pc
= (Elf_Addr
)ptr
;
731 plabel
.hppa_plabel_sl
= (Elf_Addr
)(obj
->pltgot
);
732 f
= (void (*)(void))RTLD_MAKE_PLABEL(&plabel
);
738 _rtld_call_function_addr(const Obj_Entry
*obj
, Elf_Addr ptr
)
740 volatile hppa_plabel plabel
;
743 plabel
.hppa_plabel_pc
= (Elf_Addr
)ptr
;
744 plabel
.hppa_plabel_sl
= (Elf_Addr
)(obj
->pltgot
);
745 f
= (Elf_Addr (*)(void))RTLD_MAKE_PLABEL(&plabel
);