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