1 /* $NetBSD: ppc_reloc.c,v 1.42 2009/05/24 20:35:41 he Exp $ */
4 * Copyright (C) 1998 Tsubai Masanari
5 * Portions copyright 2002 Charles M. Hannum <root@ihack.net>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: ppc_reloc.c,v 1.42 2009/05/24 20:35:41 he Exp $");
40 #include <sys/types.h>
42 #include <machine/cpu.h>
47 void _rtld_powerpc_pltcall(Elf_Word
);
48 void _rtld_powerpc_pltresolve(Elf_Word
, Elf_Word
);
50 #define ha(x) ((((u_int32_t)(x) & 0x8000) ? \
51 ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
52 #define l(x) ((u_int32_t)(x) & 0xffff)
54 void _rtld_bind_start(void);
55 void _rtld_relocate_nonplt_self(Elf_Dyn
*, Elf_Addr
);
56 caddr_t
_rtld_bind(const Obj_Entry
*, Elf_Word
);
57 static inline int _rtld_relocate_plt_object(const Obj_Entry
*,
58 const Elf_Rela
*, int, Elf_Addr
*);
61 * The PPC PLT format consists of three sections:
62 * (1) The "pltcall" and "pltresolve" glue code. This is always 18 words.
63 * (2) The code part of the PLT entries. There are 2 words per entry for
64 * up to 8192 entries, then 4 words per entry for any additional entries.
65 * (3) The data part of the PLT entries, comprising a jump table.
66 * This section is half the size of the second section (ie. 1 or 2 words
71 * Setup the plt glue routines.
73 #define PLTCALL_SIZE 20
74 #define PLTRESOLVE_SIZE 24
77 _rtld_setup_pltgot(const Obj_Entry
*obj
)
79 Elf_Word
*pltcall
, *pltresolve
;
81 int N
= obj
->pltrelalim
- obj
->pltrela
;
83 /* Entries beyond 8192 take twice as much space. */
87 pltcall
= obj
->pltgot
;
88 jmptab
= pltcall
+ 18 + N
* 2;
90 memcpy(pltcall
, _rtld_powerpc_pltcall
, PLTCALL_SIZE
);
91 pltcall
[1] |= ha(jmptab
);
92 pltcall
[2] |= l(jmptab
);
94 pltresolve
= obj
->pltgot
+ 8;
96 memcpy(pltresolve
, _rtld_powerpc_pltresolve
, PLTRESOLVE_SIZE
);
97 pltresolve
[0] |= ha(_rtld_bind_start
);
98 pltresolve
[1] |= l(_rtld_bind_start
);
99 pltresolve
[3] |= ha(obj
);
100 pltresolve
[4] |= l(obj
);
103 * Invalidate the icache for only the code part of the PLT
104 * (and not the jump table at the end).
106 __syncicache(pltcall
, (char *)jmptab
- (char *)pltcall
);
110 _rtld_relocate_nonplt_self(Elf_Dyn
*dynp
, Elf_Addr relocbase
)
112 const Elf_Rela
*rela
= 0, *relalim
;
116 for (; dynp
->d_tag
!= DT_NULL
; dynp
++) {
117 switch (dynp
->d_tag
) {
119 rela
= (const Elf_Rela
*)(relocbase
+ dynp
->d_un
.d_ptr
);
122 relasz
= dynp
->d_un
.d_val
;
126 relalim
= (const Elf_Rela
*)((const uint8_t *)rela
+ relasz
);
127 for (; rela
< relalim
; rela
++) {
128 where
= (Elf_Addr
*)(relocbase
+ rela
->r_offset
);
129 *where
= (Elf_Addr
)(relocbase
+ rela
->r_addend
);
134 _rtld_relocate_nonplt_objects(const Obj_Entry
*obj
)
136 const Elf_Rela
*rela
;
138 for (rela
= obj
->rela
; rela
< obj
->relalim
; rela
++) {
141 const Obj_Entry
*defobj
;
143 unsigned long symnum
;
145 where
= (Elf_Addr
*)(obj
->relocbase
+ rela
->r_offset
);
146 symnum
= ELF_R_SYM(rela
->r_info
);
148 switch (ELF_R_TYPE(rela
->r_info
)) {
149 #if 1 /* XXX Should not be necessary. */
150 case R_TYPE(JMP_SLOT
):
155 case R_TYPE(32): /* word32 S + A */
156 case R_TYPE(GLOB_DAT
): /* word32 S + A */
157 def
= _rtld_find_symdef(symnum
, obj
, &defobj
, false);
161 tmp
= (Elf_Addr
)(defobj
->relocbase
+ def
->st_value
+
165 rdbg(("32/GLOB_DAT %s in %s --> %p in %s",
166 obj
->strtab
+ obj
->symtab
[symnum
].st_name
,
167 obj
->path
, (void *)*where
, defobj
->path
));
170 case R_TYPE(RELATIVE
): /* word32 B + A */
171 *where
= (Elf_Addr
)(obj
->relocbase
+ rela
->r_addend
);
172 rdbg(("RELATIVE in %s --> %p", obj
->path
,
178 * These are deferred until all other relocations have
179 * been done. All we do here is make sure that the
180 * COPY relocation is not in a shared library. They
181 * are allowed only in executable files.
183 if (obj
->isdynamic
) {
185 "%s: Unexpected R_COPY relocation in shared library",
189 rdbg(("COPY (avoid in main)"));
193 rdbg(("sym = %lu, type = %lu, offset = %p, "
194 "addend = %p, contents = %p, symbol = %s",
195 symnum
, (u_long
)ELF_R_TYPE(rela
->r_info
),
196 (void *)rela
->r_offset
, (void *)rela
->r_addend
,
198 obj
->strtab
+ obj
->symtab
[symnum
].st_name
));
199 _rtld_error("%s: Unsupported relocation type %ld "
200 "in non-PLT relocations",
201 obj
->path
, (u_long
) ELF_R_TYPE(rela
->r_info
));
209 _rtld_relocate_plt_lazy(const Obj_Entry
*obj
)
211 const Elf_Rela
*rela
;
214 for (rela
= obj
->pltrela
, reloff
= 0; rela
< obj
->pltrelalim
; rela
++, reloff
++) {
215 Elf_Word
*where
= (Elf_Word
*)(obj
->relocbase
+ rela
->r_offset
);
217 Elf_Addr
*pltresolve
;
219 assert(ELF_R_TYPE(rela
->r_info
) == R_TYPE(JMP_SLOT
));
221 pltresolve
= obj
->pltgot
+ 8;
223 if (reloff
< 32768) {
225 *where
++ = 0x39600000 | reloff
;
227 /* lis r11,ha(reloff) */
228 /* addi r11,l(reloff) */
229 *where
++ = 0x3d600000 | ha(reloff
);
230 *where
++ = 0x396b0000 | l(reloff
);
233 distance
= (Elf_Addr
)pltresolve
- (Elf_Addr
)where
;
234 *where
++ = 0x48000000 | (distance
& 0x03fffffc);
237 * Icache invalidation is not done for each entry here
238 * because we sync the entire code part of the PLT once
239 * in _rtld_setup_pltgot() after all the entries have been
242 /* __syncicache(where - 3, 12); */
249 _rtld_relocate_plt_object(const Obj_Entry
*obj
, const Elf_Rela
*rela
, int reloff
, Elf_Addr
*tp
)
251 Elf_Word
*where
= (Elf_Word
*)(obj
->relocbase
+ rela
->r_offset
);
254 const Obj_Entry
*defobj
;
257 assert(ELF_R_TYPE(rela
->r_info
) == R_TYPE(JMP_SLOT
));
259 def
= _rtld_find_symdef(ELF_R_SYM(rela
->r_info
), obj
, &defobj
, true);
263 value
= (Elf_Addr
)(defobj
->relocbase
+ def
->st_value
);
264 distance
= value
- (Elf_Addr
)where
;
265 rdbg(("bind now/fixup in %s --> new=%p",
266 defobj
->strtab
+ def
->st_name
, (void *)value
));
268 if (abs(distance
) < 32*1024*1024) { /* inside 32MB? */
269 /* b value # branch directly */
270 *where
= 0x48000000 | (distance
& 0x03fffffc);
271 __syncicache(where
, 4);
273 Elf_Addr
*pltcall
, *jmptab
;
274 int N
= obj
->pltrelalim
- obj
->pltrela
;
276 /* Entries beyond 8192 take twice as much space. */
280 pltcall
= obj
->pltgot
;
281 jmptab
= pltcall
+ 18 + N
* 2;
283 jmptab
[reloff
] = value
;
285 if (reloff
< 32768) {
287 *where
++ = 0x39600000 | reloff
;
289 /* lis r11,ha(reloff) */
290 /* addi r11,l(reloff) */
291 *where
++ = 0x3d600000 | ha(reloff
);
292 *where
++ = 0x396b0000 | l(reloff
);
295 distance
= (Elf_Addr
)pltcall
- (Elf_Addr
)where
;
296 *where
++ = 0x48000000 | (distance
& 0x03fffffc);
297 __syncicache(where
- 3, 12);
306 _rtld_bind(const Obj_Entry
*obj
, Elf_Word reloff
)
308 const Elf_Rela
*rela
= obj
->pltrela
+ reloff
;
312 new_value
= 0; /* XXX gcc */
314 err
= _rtld_relocate_plt_object(obj
, rela
, reloff
, &new_value
);
315 if (err
|| new_value
== 0)
318 return (caddr_t
)new_value
;
322 _rtld_relocate_plt_objects(const Obj_Entry
*obj
)
324 const Elf_Rela
*rela
;
327 for (rela
= obj
->pltrela
, reloff
= 0; rela
< obj
->pltrelalim
; rela
++, reloff
++) {
328 if (_rtld_relocate_plt_object(obj
, rela
, reloff
, NULL
) < 0)