xref: /titanic_50/usr/src/cmd/sgs/rtld/sparc/sparc_a.out.c (revision 3db3491215579980a91e230cf21b20608fbb8259)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  *	Copyright (c) 1988 AT&T
29  *	All Rights Reserved
30  */
31 
32 /*
33  * SPARC machine dependent and a.out format file class dependent functions.
34  * Contains routines for performing function binding and symbol relocations.
35  */
36 
37 #include	<stdio.h>
38 #include	<sys/types.h>
39 #include	<sys/mman.h>
40 #include	<synch.h>
41 #include	<dlfcn.h>
42 #include	<debug.h>
43 #include	"_a.out.h"
44 #include	"_rtld.h"
45 #include	"_audit.h"
46 #include	"msg.h"
47 
48 extern void	iflush_range(caddr_t, size_t);
49 
50 /*
51  * Function binding routine - invoked on the first call to a function through
52  * the procedure linkage table;
53  * passes first through an assembly language interface.
54  *
55  * Takes the address of the PLT entry where the call originated,
56  * the offset into the relocation table of the associated
57  * relocation entry and the address of the link map (rt_private_map struct)
58  * for the entry.
59  *
60  * Returns the address of the function referenced after re-writing the PLT
61  * entry to invoke the function directly.
62  *
63  * On error, causes process to terminate with a signal.
64  */
65 ulong_t
66 aout_bndr(caddr_t pc)
67 {
68 	Rt_map		*lmp, *nlmp, *llmp;
69 	struct relocation_info *rp;
70 	struct nlist	*sp;
71 	Sym		*sym;
72 	char		*name;
73 	int 		rndx, entry;
74 	ulong_t		symval;
75 	Slookup		sl;
76 	uint_t		binfo;
77 	Lm_list		*lml;
78 
79 	/*
80 	 * For compatibility with libthread (TI_VERSION 1) we track the entry
81 	 * value.  A zero value indicates we have recursed into ld.so.1 to
82 	 * further process a locking request (see comments in completion()).
83 	 * Under this recursion we disable tsort and cleanup activities.
84 	 */
85 	entry = enter(0);
86 
87 	for (lmp = lml_main.lm_head; lmp; lmp = NEXT_RT_MAP(lmp)) {
88 		if (FCT(lmp) == &aout_fct) {
89 			if (pc > (caddr_t)(LM2LP(lmp)->lp_plt) &&
90 			    pc < (caddr_t)((int)LM2LP(lmp)->lp_plt +
91 			    AOUTDYN(lmp)->v2->ld_plt_sz))  {
92 				break;
93 			}
94 		}
95 	}
96 
97 #define	LAST22BITS	0x3fffff
98 
99 	/* LINTED */
100 	rndx = *(int *)(pc + (sizeof (ulong_t *) * 2)) & LAST22BITS;
101 	rp = &LM2LP(lmp)->lp_rp[rndx];
102 	sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
103 	name = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
104 
105 	/*
106 	 * Determine the last link-map of this list, this'll be the starting
107 	 * point for any tsort() processing.
108 	 */
109 	lml = LIST(lmp);
110 	llmp = lml->lm_tail;
111 
112 	/*
113 	 * Find definition for symbol.  Initialize the symbol lookup data
114 	 * structure.
115 	 */
116 	SLOOKUP_INIT(sl, name, lmp, lml->lm_head, ld_entry_cnt, 0, 0, 0, 0,
117 	    LKUP_DEFT);
118 
119 	if ((sym = aout_lookup_sym(&sl, &nlmp, &binfo, NULL)) == 0) {
120 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
121 		    demangle(name));
122 		rtldexit(lml, 1);
123 	}
124 
125 	symval = sym->st_value;
126 	if (!(FLAGS(nlmp) & FLG_RT_FIXED) &&
127 	    (sym->st_shndx != SHN_ABS))
128 		symval += (int)(ADDR(nlmp));
129 	if ((lmp != nlmp) && ((FLAGS1(nlmp) & FL1_RT_NOINIFIN) == 0)) {
130 		/*
131 		 * Record that this new link map is now bound to the caller.
132 		 */
133 		if (bind_one(lmp, nlmp, BND_REFER) == 0)
134 			rtldexit(lml, 1);
135 	}
136 
137 	/*
138 	 * Print binding information and rebuild PLT entry.
139 	 */
140 	DBG_CALL(Dbg_bind_global(lmp, (Addr)(ADDR(lmp) + rp->r_address),
141 	    (Off)rp->r_address, (Xword)(-1), PLT_T_NONE, nlmp,
142 	    (Addr)symval, sym->st_value, name, binfo));
143 
144 	if (!(rtld_flags & RT_FL_NOBIND))
145 		aout_plt_write((caddr_t)(ADDR(lmp) + rp->r_address), symval);
146 
147 	/*
148 	 * Complete any processing for newly loaded objects.  Note we don't
149 	 * know exactly where any new objects are loaded (we know the object
150 	 * that supplied the symbol, but others may have been loaded lazily as
151 	 * we searched for the symbol), so sorting starts from the last
152 	 * link-map know on entry to this routine.
153 	 */
154 	if (entry)
155 		load_completion(llmp);
156 
157 	/*
158 	 * If the object we've bound to is in the process of being initialized
159 	 * by another thread, determine whether we should block.
160 	 */
161 	is_dep_ready(nlmp, lmp, DBG_WAIT_SYMBOL);
162 
163 	/*
164 	 * Make sure the object to which we've bound has had it's .init fired.
165 	 * Cleanup before return to user code.
166 	 */
167 	if (entry) {
168 		is_dep_init(nlmp, lmp);
169 		leave(lml, 0);
170 	}
171 
172 	return (symval);
173 }
174 
175 
176 #define	IS_PC_RELATIVE(X) (pc_rel_type[(X)] == 1)
177 
178 static const uchar_t pc_rel_type[] = {
179 	0,				/* RELOC_8 */
180 	0,				/* RELOC_16 */
181 	0,				/* RELOC_32 */
182 	1,				/* RELOC_DISP8 */
183 	1,				/* RELOC_DISP16 */
184 	1,				/* RELOC_DISP32 */
185 	1,				/* RELOC_WDISP30 */
186 	1,				/* RELOC_WDISP22 */
187 	0,				/* RELOC_HI22 */
188 	0,				/* RELOC_22 */
189 	0,				/* RELOC_13 */
190 	0,				/* RELOC_LO10 */
191 	0,				/* RELOC_SFA_BASE */
192 	0,				/* RELOC_SFA_OFF13 */
193 	0,				/* RELOC_BASE10 */
194 	0,				/* RELOC_BASE13 */
195 	0,				/* RELOC_BASE22 */
196 	0,				/* RELOC_PC10 */
197 	0,				/* RELOC_PC22 */
198 	0,				/* RELOC_JMP_TBL */
199 	0,				/* RELOC_SEGOFF16 */
200 	0,				/* RELOC_GLOB_DAT */
201 	0,				/* RELOC_JMP_SLOT */
202 	0				/* RELOC_RELATIVE */
203 };
204 
205 int
206 aout_reloc(Rt_map * lmp, uint_t plt, int *in_nfavl)
207 {
208 	int		k;		/* loop temporary */
209 	int		nr;		/* number of relocations */
210 	char		*name;		/* symbol being searched for */
211 	long		*et;		/* cached _etext of object */
212 	long		value;		/* relocation temporary */
213 	long		*ra;		/* cached relocation address */
214 	struct relocation_info *rp;	/* current relocation */
215 	struct nlist	*sp;		/* symbol table of "symbol" */
216 	Rt_map *	_lmp;		/* lm which holds symbol definition */
217 	Sym *		sym;		/* symbol definition */
218 	int		textrel = 0, ret = 1;
219 	APlist		*bound = NULL;
220 	Lm_list		*lml = LIST(lmp);
221 
222 	DBG_CALL(Dbg_reloc_run(lmp, SHT_RELA, plt, DBG_REL_START));
223 
224 	/*
225 	 * If we've been called upon to promote an RTLD_LAZY object to an
226 	 * RTLD_NOW don't bother to do anything - a.out's are bound as if
227 	 * RTLD_NOW regardless.
228 	 */
229 	if (plt)
230 		return (1);
231 
232 	rp = LM2LP(lmp)->lp_rp;
233 	et = (long *)ETEXT(lmp);
234 	nr = GETRELSZ(AOUTDYN(lmp)) / sizeof (struct relocation_info);
235 
236 	/*
237 	 * Initialize _PLT_, if any.
238 	 */
239 	if (AOUTDYN(lmp)->v2->ld_plt_sz)
240 		aout_plt_write((caddr_t)LM2LP(lmp)->lp_plt->jb_inst,
241 		    (ulong_t)aout_rtbndr);
242 
243 	/*
244 	 * Loop through relocations.
245 	 */
246 	for (k = 0; k < nr; k++, rp++) {
247 		/* LINTED */
248 		ra = (long *)&((char *)ADDR(lmp))[rp->r_address];
249 
250 		/*
251 		 * Check to see if we're relocating in the text segment
252 		 * and turn off the write protect if necessary.
253 		 */
254 		if ((ra < et) && (textrel == 0)) {
255 			if (aout_set_prot(lmp, PROT_WRITE) == 0) {
256 				ret = 0;
257 				break;
258 			}
259 			textrel = 1;
260 		}
261 
262 		/*
263 		 * Perform the relocation.
264 		 */
265 		if (rp->r_extern == 0) {
266 			name = (char *)0;
267 			value = ADDR(lmp);
268 		} else {
269 			Slookup		sl;
270 			uint_t		binfo;
271 
272 			if (rp->r_type == RELOC_JMP_SLOT)
273 				continue;
274 			sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
275 			name = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
276 
277 			/*
278 			 * Locate symbol.  Initialize the symbol lookup data
279 			 * structure.
280 			 */
281 			SLOOKUP_INIT(sl, name, lmp, 0, ld_entry_cnt, 0, 0, 0, 0,
282 			    LKUP_STDRELOC);
283 
284 			if ((sym = aout_lookup_sym(&sl, &_lmp,
285 			    &binfo, in_nfavl)) == 0) {
286 				if (lml->lm_flags & LML_FLG_TRC_WARN) {
287 					(void)
288 					    printf(MSG_INTL(MSG_LDD_SYM_NFOUND),
289 					    demangle(name), NAME(lmp));
290 					continue;
291 				} else {
292 					eprintf(lml, ERR_FATAL,
293 					    MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
294 					    demangle(name));
295 					ret = 0;
296 					break;
297 				}
298 			}
299 
300 			/*
301 			 * If symbol was found in an object other than the
302 			 * referencing object then record the binding.
303 			 */
304 			if ((lmp != _lmp) &&
305 			    ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) {
306 				if (aplist_test(&bound, _lmp,
307 				    AL_CNT_RELBIND) == 0) {
308 					ret = 0;
309 					break;
310 				}
311 			}
312 
313 			value = sym->st_value + rp->r_addend;
314 			if (!(FLAGS(_lmp) & FLG_RT_FIXED) &&
315 			    (sym->st_shndx != SHN_COMMON) &&
316 			    (sym->st_shndx != SHN_ABS))
317 				value += ADDR(_lmp);
318 
319 			if (IS_PC_RELATIVE(rp->r_type))
320 				value -= (long)ADDR(lmp);
321 
322 			DBG_CALL(Dbg_bind_global(lmp, (Addr)ra,
323 			    (Off)(ra - ADDR(lmp)), (Xword)(-1), PLT_T_NONE,
324 			    _lmp, (Addr)value, sym->st_value, name, binfo));
325 		}
326 
327 		/*
328 		 * Perform a specific relocation operation.
329 		 */
330 		switch (rp->r_type) {
331 		case RELOC_RELATIVE:
332 			value += *ra << (32-22);
333 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
334 			    ((value >> (32 - 22)) & S_MASK(22));
335 			ra++;
336 			value += (*ra & S_MASK(10));
337 			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
338 			    (value & S_MASK(10));
339 			break;
340 		case RELOC_8:
341 		case RELOC_DISP8:
342 			value += *ra & S_MASK(8);
343 			if (!S_INRANGE(value, 8)) {
344 				eprintf(lml, ERR_FATAL,
345 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
346 				    (name ? demangle(name) :
347 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 8,
348 				    (uint_t)ra);
349 			}
350 			*ra = value;
351 			break;
352 		case RELOC_LO10:
353 		case RELOC_BASE10:
354 			value += *ra & S_MASK(10);
355 			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
356 			    (value & S_MASK(10));
357 			break;
358 		case RELOC_BASE13:
359 		case RELOC_13:
360 			value += *ra & S_MASK(13);
361 			*(long *)ra = (*(long *)ra & ~S_MASK(13)) |
362 			    (value & S_MASK(13));
363 			break;
364 		case RELOC_16:
365 		case RELOC_DISP16:
366 			value += *ra & S_MASK(16);
367 			if (!S_INRANGE(value, 16)) {
368 				eprintf(lml, ERR_FATAL,
369 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
370 				    (name ? demangle(name) :
371 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 16,
372 				    (uint_t)ra);
373 			}
374 			*(short *)ra = value;
375 			break;
376 		case RELOC_22:
377 		case RELOC_BASE22:
378 			value += *ra & S_MASK(22);
379 			if (!S_INRANGE(value, 22)) {
380 				eprintf(lml, ERR_FATAL,
381 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
382 				    (name ? demangle(name) :
383 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
384 				    (uint_t)ra);
385 			}
386 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
387 			    (value & S_MASK(22));
388 			break;
389 		case RELOC_HI22:
390 			value += (*ra & S_MASK(22)) << (32 - 22);
391 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
392 			    ((value >> (32 - 22)) & S_MASK(22));
393 			break;
394 		case RELOC_WDISP22:
395 			value += *ra & S_MASK(22);
396 			value >>= 2;
397 			if (!S_INRANGE(value, 22)) {
398 				eprintf(lml, ERR_FATAL,
399 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
400 				    (name ? demangle(name) :
401 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
402 				    (uint_t)ra);
403 			}
404 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
405 			    (value & S_MASK(22));
406 			break;
407 		case RELOC_WDISP30:
408 			value += *ra & S_MASK(30);
409 			value >>= 2;
410 			*(long *)ra = (*(long *)ra & ~S_MASK(30)) |
411 			    (value & S_MASK(30));
412 			break;
413 		case RELOC_32:
414 		case RELOC_GLOB_DAT:
415 		case RELOC_DISP32:
416 			value += *ra;
417 			*(long *)ra = value;
418 			break;
419 		default:
420 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_UNIMPL),
421 			    NAME(lmp), (name ? demangle(name) :
422 			    MSG_INTL(MSG_STR_UNKNOWN)), rp->r_type);
423 			ret = 0;
424 			break;
425 		}
426 
427 		/*
428 		 * If this relocation is against a text segment we must make
429 		 * sure that the instruction cache is flushed.
430 		 */
431 		if (textrel) {
432 			if (rp->r_type == RELOC_RELATIVE)
433 				iflush_range((caddr_t)(ra - 1), 0x8);
434 			else
435 				iflush_range((caddr_t)ra, 0x4);
436 		}
437 	}
438 
439 	return (relocate_finish(lmp, bound, textrel, ret));
440 }
441