1 /*	$NetBSD: mips_reloc.c,v 1.37 2002/10/05 11:59:05 mycroft Exp $	*/
2 
3 /*
4  * Copyright 1997 Michael L. Hitch <mhitch@montana.edu>
5  * Portions copyright 2002 Charles M. Hannum <root@ihack.net>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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.
18  *
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 OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <string.h>
34 
35 #include "debug.h"
36 #include "rtld.h"
37 
38 #define SUPPORT_OLD_BROKEN_LD
39 
40 void _rtld_bind_start(void);
41 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
42 caddr_t _rtld_bind(Elf_Word, Elf_Addr, Elf_Addr, Elf_Addr);
43 
44 void
45 _rtld_setup_pltgot(obj)
46 	const Obj_Entry *obj;
47 {
48 	obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
49 	/* XXX only if obj->pltgot[1] & 0x80000000 ?? */
50 	obj->pltgot[1] |= (Elf_Addr) obj;
51 }
52 
53 void
54 _rtld_relocate_nonplt_self(dynp, relocbase)
55 	Elf_Dyn *dynp;
56 	Elf_Addr relocbase;
57 {
58 	const Elf_Rel *rel = 0, *rellim;
59 	Elf_Addr relsz = 0;
60 	Elf_Addr *where;
61 	const Elf_Sym *symtab, *sym;
62 	Elf_Addr *got;
63 	Elf_Word local_gotno, symtabno, gotsym;
64 	int i;
65 
66 	for (; dynp->d_tag != DT_NULL; dynp++) {
67 		switch (dynp->d_tag) {
68 		case DT_REL:
69 			rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
70 			break;
71 		case DT_RELSZ:
72 			relsz = dynp->d_un.d_val;
73 			break;
74 		case DT_SYMTAB:
75 			symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr);
76 			break;
77 		case DT_PLTGOT:
78 			got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr);
79 			break;
80 		case DT_MIPS_LOCAL_GOTNO:
81 			local_gotno = dynp->d_un.d_val;
82 			break;
83 		case DT_MIPS_SYMTABNO:
84 			symtabno = dynp->d_un.d_val;
85 			break;
86 		case DT_MIPS_GOTSYM:
87 			gotsym = dynp->d_un.d_val;
88 			break;
89 		}
90 	}
91 
92 	i = (got[1] & 0x80000000) ? 2 : 1;
93 	/* Relocate the local GOT entries */
94 	got += i;
95 	for (; i < local_gotno; i++)
96 		*got++ += relocbase;
97 	sym = symtab + gotsym;
98 	/* Now do the global GOT entries */
99 	for (i = gotsym; i < symtabno; i++) {
100 		*got = sym->st_value + relocbase;
101 		++sym;
102 		++got;
103 	}
104 
105 	rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
106 	for (; rel < rellim; rel++) {
107 		where = (Elf_Addr *)(relocbase + rel->r_offset);
108 
109 		switch (ELF_R_TYPE(rel->r_info)) {
110 		case R_TYPE(NONE):
111 			break;
112 
113 		case R_TYPE(REL32):
114 			assert(ELF_R_SYM(rel->r_info) < gotsym);
115 			sym = symtab + ELF_R_SYM(rel->r_info);
116 			assert(sym->st_info ==
117 			    ELF_ST_INFO(STB_LOCAL, STT_SECTION));
118 			*where += (Elf_Addr)(sym->st_value + relocbase);
119 			break;
120 
121 		default:
122 			abort();
123 		}
124 	}
125 }
126 
127 /*
128  * It is possible for the compiler to emit relocations for unaligned data.
129  * We handle this situation with these inlines.
130  */
131 #define	RELOC_ALIGNED_P(x) \
132 	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
133 
134 static __inline Elf_Addr
135 load_ptr(void *where)
136 {
137 	Elf_Addr res;
138 
139 	memcpy(&res, where, sizeof(res));
140 
141 	return (res);
142 }
143 
144 static __inline void
145 store_ptr(void *where, Elf_Addr val)
146 {
147 
148 	memcpy(where, &val, sizeof(val));
149 }
150 
151 int
152 _rtld_relocate_nonplt_objects(obj)
153 	const Obj_Entry *obj;
154 {
155 	const Elf_Rel *rel;
156 	Elf_Addr *got = obj->pltgot;
157 	const Elf_Sym *sym, *def;
158 	const Obj_Entry *defobj;
159 	int i;
160 #ifdef SUPPORT_OLD_BROKEN_LD
161 	int broken;
162 #endif
163 
164 #ifdef SUPPORT_OLD_BROKEN_LD
165 	broken = 0;
166 	sym = obj->symtab;
167 	for (i = 1; i < 12; i++)
168 		if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE))
169 			broken = 1;
170 	dbg(("%s: broken=%d", obj->path, broken));
171 #endif
172 
173 	i = (got[1] & 0x80000000) ? 2 : 1;
174 	/* Relocate the local GOT entries */
175 	got += i;
176 	for (; i < obj->local_gotno; i++)
177 		*got++ += (Elf_Addr)obj->relocbase;
178 	sym = obj->symtab + obj->gotsym;
179 	/* Now do the global GOT entries */
180 	for (i = obj->gotsym; i < obj->symtabno; i++) {
181 		rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym,
182 		    sym->st_name + obj->strtab, *got));
183 
184 #ifdef SUPPORT_OLD_BROKEN_LD
185 		if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
186 		    broken && sym->st_shndx == SHN_UNDEF) {
187 			/*
188 			 * XXX DANGER WILL ROBINSON!
189 			 * You might think this is stupid, as it intentionally
190 			 * defeats lazy binding -- and you'd be right.
191 			 * Unfortunately, for lazy binding to work right, we
192 			 * need to a way to force the GOT slots used for
193 			 * function pointers to be resolved immediately.  This
194 			 * is supposed to be done automatically by the linker,
195 			 * by not outputting a PLT slot and setting st_value
196 			 * to 0 if there are non-PLT references, but older
197 			 * versions of GNU ld do not do this.
198 			 */
199 			def = _rtld_find_symdef(i, obj, &defobj, true);
200 			if (def == NULL)
201 				return -1;
202 			*got = def->st_value + (Elf_Addr)defobj->relocbase;
203 		} else
204 #endif
205 		if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
206 		    sym->st_value != 0) {
207 			/*
208 			 * If there are non-PLT references to the function,
209 			 * st_value should be 0, forcing us to resolve the
210 			 * address immediately.
211 			 */
212 			*got = sym->st_value + (Elf_Addr)obj->relocbase;
213 		} else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) {
214 			/* Symbols with index SHN_ABS are not relocated. */
215 			if (sym->st_shndx != SHN_ABS)
216 				*got = sym->st_value +
217 				    (Elf_Addr)obj->relocbase;
218 		} else {
219 			def = _rtld_find_symdef(i, obj, &defobj, true);
220 			if (def == NULL)
221 				return -1;
222 			*got = def->st_value + (Elf_Addr)defobj->relocbase;
223 		}
224 
225 		rdbg(("  --> now %x", *got));
226 		++sym;
227 		++got;
228 	}
229 
230 	got = obj->pltgot;
231 	for (rel = obj->rel; rel < obj->rellim; rel++) {
232 		Elf_Addr        *where, tmp;
233 		unsigned long	 symnum;
234 
235 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
236 		symnum = ELF_R_SYM(rel->r_info);
237 
238 		switch (ELF_R_TYPE(rel->r_info)) {
239 		case R_TYPE(NONE):
240 			break;
241 
242 		case R_TYPE(REL32):
243 			/* 32-bit PC-relative reference */
244 			def = obj->symtab + symnum;
245 
246 			if (symnum >= obj->gotsym) {
247 				tmp = *where;
248 				tmp += got[obj->local_gotno + symnum - obj->gotsym];
249 				*where = tmp;
250 
251 				rdbg(("REL32/G %s in %s --> %p in %s",
252 				    obj->strtab + def->st_name, obj->path,
253 				    (void *)tmp, obj->path));
254 				break;
255 			} else {
256 				/*
257 				 * XXX: ABI DIFFERENCE!
258 				 *
259 				 * Old NetBSD binutils would generate shared
260 				 * libs with section-relative relocations being
261 				 * already adjusted for the start address of
262 				 * the section.
263 				 *
264 				 * New binutils, OTOH, generate shared libs
265 				 * with the same relocations being based at
266 				 * zero, so we need to add in the start address
267 				 * of the section.
268 				 *
269 				 * --rkb, Oct 6, 2001
270 				 */
271 				tmp = *where;
272 
273 				if (def->st_info ==
274 				    ELF_ST_INFO(STB_LOCAL, STT_SECTION)
275 #ifdef SUPPORT_OLD_BROKEN_LD
276 				    && !broken
277 #endif
278 				    )
279 					tmp += (Elf_Addr)def->st_value;
280 
281 				tmp += (Elf_Addr)obj->relocbase;
282 				*where = tmp;
283 
284 				rdbg(("REL32/L %s in %s --> %p in %s",
285 				    obj->strtab + def->st_name, obj->path,
286 				    (void *)tmp, obj->path));
287 			}
288 			break;
289 
290 		default:
291 			rdbg(("sym = %lu, type = %lu, offset = %p, "
292 			    "contents = %p, symbol = %s",
293 			    symnum, (u_long)ELF_R_TYPE(rel->r_info),
294 			    (void *)rel->r_offset, (void *)load_ptr(where),
295 			    obj->strtab + obj->symtab[symnum].st_name));
296 			_rtld_error("%s: Unsupported relocation type %ld "
297 			    "in non-PLT relocations\n",
298 			    obj->path, (u_long) ELF_R_TYPE(rel->r_info));
299 			return -1;
300 		}
301 	}
302 
303 	return 0;
304 }
305 
306 int
307 _rtld_relocate_plt_lazy(obj)
308 	const Obj_Entry *obj;
309 {
310 	/* PLT fixups were done above in the GOT relocation. */
311 	return 0;
312 }
313 
314 caddr_t
315 _rtld_bind(a0, a1, a2, a3)
316 	Elf_Word a0;
317 	Elf_Addr a1, a2, a3;
318 {
319 	Elf_Addr *got = (Elf_Addr *)(a2 - 0x7ff0);
320 	const Obj_Entry *obj = (Obj_Entry *)(got[1] & 0x7fffffff);
321 	const Elf_Sym *def;
322 	const Obj_Entry *defobj;
323 	Elf_Addr new_value;
324 
325 	def = _rtld_find_symdef(a0, obj, &defobj, true);
326 	if (def == NULL)
327 		_rtld_die();
328 
329 	new_value = (Elf_Addr)(defobj->relocbase + def->st_value);
330 	rdbg(("bind now/fixup in %s --> new=%p",
331 	    defobj->strtab + def->st_name, (void *)new_value));
332 	got[obj->local_gotno + a0 - obj->gotsym] = new_value;
333 	return ((caddr_t)new_value);
334 }
335