1 /* $NetBSD: alpha_reloc.c,v 1.40 2011/03/31 15:30:31 skrll Exp $ */
4 * Copyright (c) 2001 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
40 * All rights reserved.
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions
45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the distribution.
51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
52 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
53 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
54 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
55 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
56 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
60 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63 #include <sys/cdefs.h>
65 __RCSID("$NetBSD: alpha_reloc.c,v 1.40 2011/03/31 15:30:31 skrll Exp $");
68 #include <sys/types.h>
75 #ifdef RTLD_DEBUG_ALPHA
76 #define adbg(x) xprintf x
78 #define adbg(x) /* nothing */
81 void _rtld_bind_start(void);
82 void _rtld_bind_start_old(void);
83 void _rtld_relocate_nonplt_self(Elf_Dyn
*, Elf_Addr
);
84 caddr_t
_rtld_bind(const Obj_Entry
*, Elf_Addr
);
85 static inline int _rtld_relocate_plt_object(const Obj_Entry
*,
86 const Elf_Rela
*, Elf_Addr
*);
89 _rtld_setup_pltgot(const Obj_Entry
*obj
)
94 * The PLTGOT on the Alpha looks like this:
110 * The old-format entries look like (displacements filled in
113 * ldah $28, 0($31) # 0x279f0000
114 * lda $28, 0($28) # 0x239c0000
115 * br $31, plt0 # 0xc3e00000
117 * The new-format entries look like:
119 * br $28, plt0 # 0xc3800000
123 * What we do is fetch the first PLT entry and check to
124 * see the first word of it matches the first word of the
125 * old format. If so, we use a binding routine that can
126 * handle the old format, otherwise we use a binding routine
127 * that handles the new format.
129 * Note that this is done on a per-object basis, we can mix
130 * and match shared objects build with both the old and new
133 word0
= *(uint32_t *)(((char *) obj
->pltgot
) + 32);
134 if ((word0
& 0xffff0000) == 0x279f0000) {
135 /* Old PLT entry format. */
136 adbg(("ALPHA: object %p has old PLT format\n", obj
));
137 obj
->pltgot
[2] = (Elf_Addr
) &_rtld_bind_start_old
;
138 obj
->pltgot
[3] = (Elf_Addr
) obj
;
140 /* New PLT entry format. */
141 adbg(("ALPHA: object %p has new PLT format\n", obj
));
142 obj
->pltgot
[2] = (Elf_Addr
) &_rtld_bind_start
;
143 obj
->pltgot
[3] = (Elf_Addr
) obj
;
146 __asm
volatile("imb");
150 * It is possible for the compiler to emit relocations for unaligned data.
151 * We handle this situation with these inlines.
153 #define RELOC_ALIGNED_P(x) \
154 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
156 static inline Elf_Addr
157 load_ptr(void *where
)
161 memcpy(&res
, where
, sizeof(res
));
167 store_ptr(void *where
, Elf_Addr val
)
170 memcpy(where
, &val
, sizeof(val
));
174 _rtld_relocate_nonplt_self(Elf_Dyn
*dynp
, Elf_Addr relocbase
)
176 const Elf_Rela
*rela
= 0, *relalim
;
180 for (; dynp
->d_tag
!= DT_NULL
; dynp
++) {
181 switch (dynp
->d_tag
) {
183 rela
= (const Elf_Rela
*)(relocbase
+ dynp
->d_un
.d_ptr
);
186 relasz
= dynp
->d_un
.d_val
;
190 relalim
= (const Elf_Rela
*)((const uint8_t *)rela
+ relasz
);
191 for (; rela
< relalim
; rela
++) {
192 where
= (Elf_Addr
*)(relocbase
+ rela
->r_offset
);
193 /* XXX For some reason I see a few GLOB_DAT relocs here. */
194 *where
+= (Elf_Addr
)relocbase
;
199 _rtld_relocate_nonplt_objects(Obj_Entry
*obj
)
201 const Elf_Rela
*rela
;
202 Elf_Addr target
= -1;
204 for (rela
= obj
->rela
; rela
< obj
->relalim
; rela
++) {
207 const Obj_Entry
*defobj
;
209 unsigned long symnum
;
211 where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
212 symnum
= ELF_R_SYM(rela
->r_info
);
214 switch (ELF_R_TYPE(rela
->r_info
)) {
218 case R_TYPE(REFQUAD
):
219 case R_TYPE(GLOB_DAT
):
220 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
223 target
= (Elf_Addr
)(defobj
->relocbase
+
226 tmp
= target
+ rela
->r_addend
;
227 if (__predict_true(RELOC_ALIGNED_P(where
))) {
231 if (load_ptr(where
) != tmp
)
232 store_ptr(where
, tmp
);
234 rdbg(("REFQUAD/GLOB_DAT %s in %s --> %p in %s",
235 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
236 obj
->path
, (void *)tmp
, defobj
->path
));
239 case R_TYPE(RELATIVE
):
240 if (__predict_true(RELOC_ALIGNED_P(where
)))
241 *where
+= (Elf_Addr
)obj
->relocbase
;
244 load_ptr(where
) + (Elf_Addr
)obj
->relocbase
);
245 rdbg(("RELATIVE in %s --> %p", obj
->path
,
251 * These are deferred until all other relocations have
252 * been done. All we do here is make sure that the
253 * COPY relocation is not in a shared library. They
254 * are allowed only in executable files.
256 if (obj
->isdynamic
) {
258 "%s: Unexpected R_COPY relocation in shared library",
262 rdbg(("COPY (avoid in main)"));
265 case R_TYPE(TPREL64
):
266 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
270 if (!defobj
->tls_done
&&
271 _rtld_tls_offset_allocate(obj
))
274 tmp
= (Elf64_Addr
)(def
->st_value
+
275 sizeof(struct tls_tcb
) + defobj
->tlsoffset
+
278 if (__predict_true(RELOC_ALIGNED_P(where
)))
281 store_ptr(where
, tmp
);
283 rdbg(("TPREL64 %s in %s --> %p",
284 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
285 obj
->path
, (void *)*where
));
289 case R_TYPE(DTPMOD64
):
290 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
294 tmp
= (Elf64_Addr
)defobj
->tlsindex
;
295 if (__predict_true(RELOC_ALIGNED_P(where
)))
298 store_ptr(where
, tmp
);
300 rdbg(("DTPMOD64 %s in %s --> %p",
301 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
302 obj
->path
, (void *)*where
));
306 case R_TYPE(DTPREL64
):
307 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
311 tmp
= (Elf64_Addr
)(def
->st_value
+ rela
->r_addend
);
312 if (__predict_true(RELOC_ALIGNED_P(where
)))
315 store_ptr(where
, tmp
);
317 rdbg(("DTPREL64 %s in %s --> %p",
318 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
319 obj
->path
, (void *)*where
));
324 rdbg(("sym = %lu, type = %lu, offset = %p, "
325 "addend = %p, contents = %p, symbol = %s",
326 symnum
, (u_long
)ELF_R_TYPE(rela
->r_info
),
327 (void *)rela
->r_offset
, (void *)rela
->r_addend
,
328 (void *)load_ptr(where
),
329 obj
->strtab
+ obj
->symtab
[symnum
].st_name
));
330 _rtld_error("%s: Unsupported relocation type %ld "
331 "in non-PLT relocations",
332 obj
->path
, (u_long
) ELF_R_TYPE(rela
->r_info
));
340 _rtld_relocate_plt_lazy(const Obj_Entry
*obj
)
342 const Elf_Rela
*rela
;
347 for (rela
= obj
->pltrela
; rela
< obj
->pltrelalim
; rela
++) {
348 Elf_Addr
*where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
350 assert(ELF_R_TYPE(rela
->r_info
) == R_TYPE(JMP_SLOT
));
352 /* Just relocate the GOT slots pointing into the PLT */
353 *where
+= (Elf_Addr
)obj
->relocbase
;
354 rdbg(("fixup !main in %s --> %p", obj
->path
, (void *)*where
));
361 _rtld_relocate_plt_object(const Obj_Entry
*obj
, const Elf_Rela
*rela
,
364 Elf_Addr
*where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
367 const Obj_Entry
*defobj
;
369 unsigned long info
= rela
->r_info
;
371 assert(ELF_R_TYPE(info
) == R_TYPE(JMP_SLOT
));
373 def
= _rtld_find_plt_symdef(ELF_R_SYM(info
), obj
, &defobj
, tp
!= NULL
);
374 if (__predict_false(def
== NULL
))
376 if (__predict_false(def
== &_rtld_sym_zero
))
379 new_value
= (Elf_Addr
)(defobj
->relocbase
+ def
->st_value
);
380 rdbg(("bind now/fixup in %s --> old=%p new=%p",
381 defobj
->strtab
+ def
->st_name
, (void *)*where
, (void *)new_value
));
383 if ((stubaddr
= *where
) != new_value
) {
384 int64_t delta
, idisp
;
385 uint32_t insn
[3], *stubptr
;
389 /* Point this GOT entry at the target. */
393 * Alpha shared objects may have multiple GOTs, each
394 * of which may point to this entry in the PLT. But,
395 * we only have a reference to the first GOT entry which
396 * points to this PLT entry. In order to avoid having to
397 * re-bind this call every time a non-first GOT entry is
398 * used, we will attempt to patch up the PLT entry to
399 * reference the target, rather than the binder.
401 * When the PLT stub gets control, PV contains the address
402 * of the PLT entry. Each PLT entry has room for 3 insns.
403 * If the displacement of the target from PV fits in a signed
404 * 32-bit integer, we can simply add it to PV. Otherwise,
405 * we must load the GOT entry itself into PV.
407 * Note if the shared object uses the old PLT format, then
408 * we cannot patch up the PLT safely, and so we skip it
411 * [*] Actually, if we're not doing lazy-binding, then
412 * we *can* (and do) patch up this PLT entry; the PLTGOT
413 * thunk won't yet point to any binder entry point, and
414 * so this test will fail as it would for the new PLT
417 if (obj
->pltgot
[2] == (Elf_Addr
) &_rtld_bind_start_old
) {
418 rdbg((" old PLT format"));
422 delta
= new_value
- stubaddr
;
423 rdbg((" stubaddr=%p, where-stubaddr=%ld, delta=%ld",
424 (void *)stubaddr
, (long)where
- (long)stubaddr
,
427 if ((int32_t)delta
== delta
) {
429 * We can adjust PV with an LDA, LDAH sequence.
431 * First, build an LDA insn to adjust the low 16
434 insn
[insncnt
++] = 0x08 << 26 | 27 << 21 | 27 << 16 |
436 rdbg((" LDA $27,%d($27)", (int16_t)delta
));
438 * Adjust the delta to account for the effects of
439 * the LDA, including sign-extension.
441 delta
-= (int16_t)delta
;
444 * Build an LDAH instruction to adjust the
447 insn
[insncnt
++] = 0x09 << 26 | 27 << 21 |
448 27 << 16 | ((delta
>> 16) & 0xffff);
449 rdbg((" LDAH $27,%d($27)",
450 (int16_t)(delta
>> 16)));
455 /* We must load the GOT entry. */
456 delta
= (Elf_Addr
)where
- stubaddr
;
459 * If the GOT entry is too far away from the PLT
460 * entry, then we can't patch up the PLT entry.
461 * This PLT entry will have to be bound for each
462 * GOT entry except for the first one. This program
463 * will still run, albeit very slowly. It is very
464 * unlikely that this case will ever happen in
467 if ((int32_t)delta
!= delta
) {
468 rdbg((" PLT stub too far from GOT to relocate"));
471 dhigh
= delta
- (int16_t)delta
;
474 * Build an LDAH instruction to adjust the
477 insn
[insncnt
++] = 0x09 << 26 | 27 << 21 |
478 27 << 16 | ((dhigh
>> 16) & 0xffff);
479 rdbg((" LDAH $27,%d($27)",
480 (int16_t)(dhigh
>> 16)));
482 /* Build an LDQ to load the GOT entry. */
483 insn
[insncnt
++] = 0x29 << 26 | 27 << 21 |
484 27 << 16 | (delta
& 0xffff);
485 rdbg((" LDQ $27,%d($27)",
490 * Now, build a JMP or BR insn to jump to the target. If
491 * the displacement fits in a sign-extended 21-bit field,
492 * we can use the more efficient BR insn. Otherwise, we
493 * have to jump indirect through PV.
495 pc
= stubaddr
+ (4 * (insncnt
+ 1));
496 idisp
= (int64_t)(new_value
- pc
) >> 2;
497 if (-0x100000 <= idisp
&& idisp
< 0x100000) {
498 insn
[insncnt
++] = 0x30 << 26 | 31 << 21 |
500 rdbg((" BR $31,%p", (void *)new_value
));
502 insn
[insncnt
++] = 0x1a << 26 | 31 << 21 |
503 27 << 16 | (idisp
& 0x3fff);
504 rdbg((" JMP $31,($27),%d",
505 (int)(idisp
& 0x3fff)));
509 * Fill in the tail of the PLT entry first, for reentrancy.
510 * Until we have overwritten the first insn (an unconditional
511 * branch), the remaining insns have no effect.
513 stubptr
= (uint32_t *)stubaddr
;
514 while (insncnt
> 1) {
516 stubptr
[insncnt
] = insn
[insncnt
];
519 * Commit the tail of the insn sequence to memory
520 * before overwriting the first insn.
522 __asm
volatile("wmb" ::: "memory");
523 stubptr
[0] = insn
[0];
525 * I-stream will be sync'd when we either return from
526 * the binder (lazy bind case) or when the PLTGOT thunk
527 * is patched up (bind-now case).
538 _rtld_bind(const Obj_Entry
*obj
, Elf_Addr reloff
)
540 const Elf_Rela
*rela
=
541 (const Elf_Rela
*)((const uint8_t *)obj
->pltrela
+ reloff
);
542 Elf_Addr result
= 0; /* XXX gcc */
545 _rtld_shared_enter();
546 err
= _rtld_relocate_plt_object(obj
, rela
, &result
);
551 return (caddr_t
)result
;
555 _rtld_relocate_plt_objects(const Obj_Entry
*obj
)
557 const Elf_Rela
*rela
;
559 for (rela
= obj
->pltrela
; rela
< obj
->pltrelalim
; rela
++)
560 if (_rtld_relocate_plt_object(obj
, rela
, NULL
) < 0)