1 /* $NetBSD: alpha_reloc.c,v 1.32 2009/05/24 18:29:03 he 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.32 2009/05/24 18:29:03 he 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_Word
);
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(const 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)"));
266 rdbg(("sym = %lu, type = %lu, offset = %p, "
267 "addend = %p, contents = %p, symbol = %s",
268 symnum
, (u_long
)ELF_R_TYPE(rela
->r_info
),
269 (void *)rela
->r_offset
, (void *)rela
->r_addend
,
270 (void *)load_ptr(where
),
271 obj
->strtab
+ obj
->symtab
[symnum
].st_name
));
272 _rtld_error("%s: Unsupported relocation type %ld "
273 "in non-PLT relocations",
274 obj
->path
, (u_long
) ELF_R_TYPE(rela
->r_info
));
282 _rtld_relocate_plt_lazy(const Obj_Entry
*obj
)
284 const Elf_Rela
*rela
;
289 for (rela
= obj
->pltrela
; rela
< obj
->pltrelalim
; rela
++) {
290 Elf_Addr
*where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
292 assert(ELF_R_TYPE(rela
->r_info
) == R_TYPE(JMP_SLOT
));
294 /* Just relocate the GOT slots pointing into the PLT */
295 *where
+= (Elf_Addr
)obj
->relocbase
;
296 rdbg(("fixup !main in %s --> %p", obj
->path
, (void *)*where
));
303 _rtld_relocate_plt_object(const Obj_Entry
*obj
, const Elf_Rela
*rela
, Elf_Addr
*tp
)
305 Elf_Addr
*where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
308 const Obj_Entry
*defobj
;
311 assert(ELF_R_TYPE(rela
->r_info
) == R_TYPE(JMP_SLOT
));
313 def
= _rtld_find_symdef(ELF_R_SYM(rela
->r_info
), obj
, &defobj
, true);
317 new_value
= (Elf_Addr
)(defobj
->relocbase
+ def
->st_value
);
318 rdbg(("bind now/fixup in %s --> old=%p new=%p",
319 defobj
->strtab
+ def
->st_name
, (void *)*where
, (void *)new_value
));
321 if ((stubaddr
= *where
) != new_value
) {
322 int64_t delta
, idisp
;
323 uint32_t insn
[3], *stubptr
;
327 /* Point this GOT entry at the target. */
331 * Alpha shared objects may have multiple GOTs, each
332 * of which may point to this entry in the PLT. But,
333 * we only have a reference to the first GOT entry which
334 * points to this PLT entry. In order to avoid having to
335 * re-bind this call every time a non-first GOT entry is
336 * used, we will attempt to patch up the PLT entry to
337 * reference the target, rather than the binder.
339 * When the PLT stub gets control, PV contains the address
340 * of the PLT entry. Each PLT entry has room for 3 insns.
341 * If the displacement of the target from PV fits in a signed
342 * 32-bit integer, we can simply add it to PV. Otherwise,
343 * we must load the GOT entry itself into PV.
345 * Note if the shared object uses the old PLT format, then
346 * we cannot patch up the PLT safely, and so we skip it
349 * [*] Actually, if we're not doing lazy-binding, then
350 * we *can* (and do) patch up this PLT entry; the PLTGOT
351 * thunk won't yet point to any binder entry point, and
352 * so this test will fail as it would for the new PLT
355 if (obj
->pltgot
[2] == (Elf_Addr
) &_rtld_bind_start_old
) {
356 rdbg((" old PLT format"));
360 delta
= new_value
- stubaddr
;
361 rdbg((" stubaddr=%p, where-stubaddr=%ld, delta=%ld",
362 (void *)stubaddr
, (long)where
- (long)stubaddr
,
365 if ((int32_t)delta
== delta
) {
367 * We can adjust PV with an LDA, LDAH sequence.
369 * First, build an LDA insn to adjust the low 16
372 insn
[insncnt
++] = 0x08 << 26 | 27 << 21 | 27 << 16 |
374 rdbg((" LDA $27,%d($27)", (int16_t)delta
));
376 * Adjust the delta to account for the effects of
377 * the LDA, including sign-extension.
379 delta
-= (int16_t)delta
;
382 * Build an LDAH instruction to adjust the
385 insn
[insncnt
++] = 0x09 << 26 | 27 << 21 |
386 27 << 16 | ((delta
>> 16) & 0xffff);
387 rdbg((" LDAH $27,%d($27)",
388 (int16_t)(delta
>> 16)));
393 /* We must load the GOT entry. */
394 delta
= (Elf_Addr
)where
- stubaddr
;
397 * If the GOT entry is too far away from the PLT
398 * entry, then we can't patch up the PLT entry.
399 * This PLT entry will have to be bound for each
400 * GOT entry except for the first one. This program
401 * will still run, albeit very slowly. It is very
402 * unlikely that this case will ever happen in
405 if ((int32_t)delta
!= delta
) {
406 rdbg((" PLT stub too far from GOT to relocate"));
409 dhigh
= delta
- (int16_t)delta
;
412 * Build an LDAH instruction to adjust the
415 insn
[insncnt
++] = 0x09 << 26 | 27 << 21 |
416 27 << 16 | ((dhigh
>> 16) & 0xffff);
417 rdbg((" LDAH $27,%d($27)",
418 (int16_t)(dhigh
>> 16)));
420 /* Build an LDQ to load the GOT entry. */
421 insn
[insncnt
++] = 0x29 << 26 | 27 << 21 |
422 27 << 16 | (delta
& 0xffff);
423 rdbg((" LDQ $27,%d($27)",
428 * Now, build a JMP or BR insn to jump to the target. If
429 * the displacement fits in a sign-extended 21-bit field,
430 * we can use the more efficient BR insn. Otherwise, we
431 * have to jump indirect through PV.
433 pc
= stubaddr
+ (4 * (insncnt
+ 1));
434 idisp
= (int64_t)(new_value
- pc
) >> 2;
435 if (-0x100000 <= idisp
&& idisp
< 0x100000) {
436 insn
[insncnt
++] = 0x30 << 26 | 31 << 21 |
438 rdbg((" BR $31,%p", (void *)new_value
));
440 insn
[insncnt
++] = 0x1a << 26 | 31 << 21 |
441 27 << 16 | (idisp
& 0x3fff);
442 rdbg((" JMP $31,($27),%d",
443 (int)(idisp
& 0x3fff)));
447 * Fill in the tail of the PLT entry first, for reentrancy.
448 * Until we have overwritten the first insn (an unconditional
449 * branch), the remaining insns have no effect.
451 stubptr
= (uint32_t *)stubaddr
;
452 while (insncnt
> 1) {
454 stubptr
[insncnt
] = insn
[insncnt
];
457 * Commit the tail of the insn sequence to memory
458 * before overwriting the first insn.
460 __asm
volatile("wmb" ::: "memory");
461 stubptr
[0] = insn
[0];
463 * I-stream will be sync'd when we either return from
464 * the binder (lazy bind case) or when the PLTGOT thunk
465 * is patched up (bind-now case).
476 _rtld_bind(const Obj_Entry
*obj
, Elf_Word reloff
)
478 const Elf_Rela
*rela
=
479 (const Elf_Rela
*)((const uint8_t *)obj
->pltrela
+ reloff
);
483 err
= _rtld_relocate_plt_object(obj
, rela
, &result
);
484 if (err
|| result
== 0)
487 return (caddr_t
)result
;
491 _rtld_relocate_plt_objects(const Obj_Entry
*obj
)
493 const Elf_Rela
*rela
;
495 for (rela
= obj
->pltrela
; rela
< obj
->pltrelalim
; rela
++)
496 if (_rtld_relocate_plt_object(obj
, rela
, NULL
) < 0)