xref: /titanic_44/usr/src/cmd/sgs/rtld/sparc/sparc_a.out.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  *	Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	All Rights Reserved	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 #include	"_synonyms.h"
37 
38 #include	<stdio.h>
39 #include	<sys/types.h>
40 #include	<sys/mman.h>
41 #include	<synch.h>
42 #include	<dlfcn.h>
43 #include	"_a.out.h"
44 #include	"_rtld.h"
45 #include	"_audit.h"
46 #include	"msg.h"
47 #include	"debug.h"
48 
49 extern void	iflush_range(caddr_t, size_t);
50 
51 /*
52  * Function binding routine - invoked on the first call to a function through
53  * the procedure linkage table;
54  * passes first through an assembly language interface.
55  *
56  * Takes the address of the PLT entry where the call originated,
57  * the offset into the relocation table of the associated
58  * relocation entry and the address of the link map (rt_private_map struct)
59  * for the entry.
60  *
61  * Returns the address of the function referenced after re-writing the PLT
62  * entry to invoke the function directly.
63  *
64  * On error, causes process to terminate with a signal.
65  */
66 ulong_t
67 aout_bndr(caddr_t pc)
68 {
69 	Rt_map *	lmp, * nlmp, * llmp;
70 	struct relocation_info *rp;
71 	struct nlist	*sp;
72 	Sym *		sym;
73 	char		*name;
74 	int 		rndx, entry;
75 	ulong_t		symval;
76 	Slookup		sl;
77 	uint_t		binfo;
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();
86 
87 	for (lmp = lml_main.lm_head; lmp; lmp = (Rt_map *)NEXT(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 	llmp = LIST(lmp)->lm_tail;
110 
111 	/*
112 	 * Find definition for symbol.
113 	 */
114 	sl.sl_name = name;
115 	sl.sl_cmap = lmp;
116 	sl.sl_imap = LIST(lmp)->lm_head;
117 	sl.sl_hash = 0;
118 	sl.sl_rsymndx = 0;
119 	sl.sl_flags = LKUP_DEFT;
120 
121 	if ((sym = aout_lookup_sym(&sl, &nlmp, &binfo)) == 0) {
122 		eprintf(ERR_FATAL, MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
123 		    demangle(name));
124 		rtldexit(LIST(lmp), 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(LIST(lmp), 1);
137 	}
138 
139 	/*
140 	 * Print binding information and rebuild PLT entry.
141 	 */
142 	DBG_CALL(Dbg_bind_global(NAME(lmp),
143 	    (caddr_t)(ADDR(lmp) + rp->r_address), (caddr_t)rp->r_address,
144 	    (Xword)(-1), PLT_T_NONE, NAME(nlmp), (caddr_t)symval,
145 	    (caddr_t)sym->st_value, name, binfo));
146 
147 	if (!(rtld_flags & RT_FL_NOBIND))
148 		aout_plt_write((caddr_t)(ADDR(lmp) + rp->r_address), symval);
149 
150 	/*
151 	 * Complete any processing for newly loaded objects.  Note we don't
152 	 * know exactly where any new objects are loaded (we know the object
153 	 * that supplied the symbol, but others may have been loaded lazily as
154 	 * we searched for the symbol), so sorting starts from the last
155 	 * link-map know on entry to this routine.
156 	 */
157 	if (entry)
158 		load_completion(llmp, lmp);
159 
160 	/*
161 	 * If the object we've bound to is in the process of being initialized
162 	 * by another thread, determine whether we should block.
163 	 */
164 	is_dep_ready(nlmp, lmp, DBG_WAIT_SYMBOL);
165 
166 	/*
167 	 * Make sure the object to which we've bound has had it's .init fired.
168 	 * Cleanup before return to user code.
169 	 */
170 	if (entry) {
171 		is_dep_init(nlmp, lmp);
172 		leave(LIST(lmp));
173 	}
174 
175 	return (symval);
176 }
177 
178 
179 #define	IS_PC_RELATIVE(X) (pc_rel_type[(X)] == 1)
180 
181 static const uchar_t pc_rel_type[] = {
182 	0,				/* RELOC_8 */
183 	0,				/* RELOC_16 */
184 	0,				/* RELOC_32 */
185 	1,				/* RELOC_DISP8 */
186 	1,				/* RELOC_DISP16 */
187 	1,				/* RELOC_DISP32 */
188 	1,				/* RELOC_WDISP30 */
189 	1,				/* RELOC_WDISP22 */
190 	0,				/* RELOC_HI22 */
191 	0,				/* RELOC_22 */
192 	0,				/* RELOC_13 */
193 	0,				/* RELOC_LO10 */
194 	0,				/* RELOC_SFA_BASE */
195 	0,				/* RELOC_SFA_OFF13 */
196 	0,				/* RELOC_BASE10 */
197 	0,				/* RELOC_BASE13 */
198 	0,				/* RELOC_BASE22 */
199 	0,				/* RELOC_PC10 */
200 	0,				/* RELOC_PC22 */
201 	0,				/* RELOC_JMP_TBL */
202 	0,				/* RELOC_SEGOFF16 */
203 	0,				/* RELOC_GLOB_DAT */
204 	0,				/* RELOC_JMP_SLOT */
205 	0				/* RELOC_RELATIVE */
206 };
207 
208 int
209 aout_reloc(Rt_map * lmp, uint_t plt)
210 {
211 	int		k;		/* loop temporary */
212 	int		nr;		/* number of relocations */
213 	char		*name;		/* symbol being searched for */
214 	long		*et;		/* cached _etext of object */
215 	long		value;		/* relocation temporary */
216 	long		*ra;		/* cached relocation address */
217 	struct relocation_info *rp;	/* current relocation */
218 	struct nlist	*sp;		/* symbol table of "symbol" */
219 	Rt_map *	_lmp;		/* lm which holds symbol definition */
220 	Sym *		sym;		/* symbol definition */
221 	int		textrel = 0, ret = 1;
222 	Alist		*bound = 0;
223 
224 	DBG_CALL(Dbg_reloc_run(NAME(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.
281 			 */
282 			sl.sl_name = name;
283 			sl.sl_cmap = lmp;
284 			sl.sl_imap = 0;
285 			sl.sl_hash = 0;
286 			sl.sl_rsymndx = 0;
287 			sl.sl_flags = (LKUP_DEFT | LKUP_ALLCNTLIST);
288 
289 			if ((sym = aout_lookup_sym(&sl, &_lmp, &binfo)) == 0) {
290 				if (LIST(lmp)->lm_flags & LML_FLG_TRC_WARN) {
291 					(void)
292 					    printf(MSG_INTL(MSG_LDD_SYM_NFOUND),
293 					    demangle(name), NAME(lmp));
294 					continue;
295 				} else {
296 					eprintf(ERR_FATAL,
297 					    MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
298 					    demangle(name));
299 					ret = 0;
300 					break;
301 				}
302 			}
303 
304 			/*
305 			 * If symbol was found in an object other than the
306 			 * referencing object then record the binding.
307 			 */
308 			if ((lmp != _lmp) &&
309 			    ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) {
310 				if (alist_test(&bound, _lmp, sizeof (Rt_map *),
311 				    AL_CNT_RELBIND) == 0) {
312 					ret = 0;
313 					break;
314 				}
315 			}
316 
317 			value = sym->st_value + rp->r_addend;
318 			if (!(FLAGS(_lmp) & FLG_RT_FIXED) &&
319 			    (sym->st_shndx != SHN_COMMON) &&
320 			    (sym->st_shndx != SHN_ABS))
321 				value += ADDR(_lmp);
322 
323 			if (IS_PC_RELATIVE(rp->r_type))
324 				value -= (long)ADDR(lmp);
325 
326 			DBG_CALL(Dbg_bind_global(NAME(lmp), (caddr_t)ra,
327 			    (caddr_t)(ra - ADDR(lmp)), (Xword)(-1), PLT_T_NONE,
328 			    NAME(_lmp), (caddr_t)value, (caddr_t)sym->st_value,
329 			    name, binfo));
330 		}
331 
332 		/*
333 		 * Perform a specific relocation operation.
334 		 */
335 		switch (rp->r_type) {
336 		case RELOC_RELATIVE:
337 			value += *ra << (32-22);
338 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
339 				((value >> (32 - 22)) & S_MASK(22));
340 			ra++;
341 			value += (*ra & S_MASK(10));
342 			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
343 				(value & S_MASK(10));
344 			break;
345 		case RELOC_8:
346 		case RELOC_DISP8:
347 			value += *ra & S_MASK(8);
348 			if (!S_INRANGE(value, 8))
349 			    eprintf(ERR_FATAL, MSG_INTL(MSG_REL_OVERFLOW),
350 				NAME(lmp), (name ? demangle(name) :
351 				MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 8,
352 				(uint_t)ra);
353 			*ra = value;
354 			break;
355 		case RELOC_LO10:
356 		case RELOC_BASE10:
357 			value += *ra & S_MASK(10);
358 			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
359 				(value & S_MASK(10));
360 			break;
361 		case RELOC_BASE13:
362 		case RELOC_13:
363 			value += *ra & S_MASK(13);
364 			*(long *)ra = (*(long *)ra & ~S_MASK(13)) |
365 				(value & S_MASK(13));
366 			break;
367 		case RELOC_16:
368 		case RELOC_DISP16:
369 			value += *ra & S_MASK(16);
370 			if (!S_INRANGE(value, 16))
371 			    eprintf(ERR_FATAL, MSG_INTL(MSG_REL_OVERFLOW),
372 				NAME(lmp), (name ? demangle(name) :
373 				MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 16,
374 				(uint_t)ra);
375 			*(short *)ra = value;
376 			break;
377 		case RELOC_22:
378 		case RELOC_BASE22:
379 			value += *ra & S_MASK(22);
380 			if (!S_INRANGE(value, 22))
381 			    eprintf(ERR_FATAL, MSG_INTL(MSG_REL_OVERFLOW),
382 				NAME(lmp), (name ? demangle(name) :
383 				MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
384 				(uint_t)ra);
385 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
386 				(value & S_MASK(22));
387 			break;
388 		case RELOC_HI22:
389 			value += (*ra & S_MASK(22)) << (32 - 22);
390 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
391 				((value >> (32 - 22)) & S_MASK(22));
392 			break;
393 		case RELOC_WDISP22:
394 			value += *ra & S_MASK(22);
395 			value >>= 2;
396 			if (!S_INRANGE(value, 22))
397 			    eprintf(ERR_FATAL, MSG_INTL(MSG_REL_OVERFLOW),
398 				NAME(lmp), (name ? demangle(name) :
399 				MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
400 				(uint_t)ra);
401 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
402 				(value & S_MASK(22));
403 			break;
404 		case RELOC_WDISP30:
405 			value += *ra & S_MASK(30);
406 			value >>= 2;
407 			*(long *)ra = (*(long *)ra & ~S_MASK(30)) |
408 				(value & S_MASK(30));
409 			break;
410 		case RELOC_32:
411 		case RELOC_GLOB_DAT:
412 		case RELOC_DISP32:
413 			value += *ra;
414 			*(long *)ra = value;
415 			break;
416 		default:
417 			eprintf(ERR_FATAL, MSG_INTL(MSG_REL_UNIMPL), NAME(lmp),
418 			    (name ? demangle(name) : MSG_INTL(MSG_STR_UNKNOWN)),
419 			    rp->r_type);
420 			ret = 0;
421 			break;
422 		}
423 
424 		/*
425 		 * If this relocation is against a text segment we must make
426 		 * sure that the instruction cache is flushed.
427 		 */
428 		if (textrel) {
429 			if (rp->r_type == RELOC_RELATIVE)
430 				iflush_range((caddr_t)(ra - 1), 0x8);
431 			else
432 				iflush_range((caddr_t)ra, 0x4);
433 		}
434 	}
435 
436 	return (relocate_finish(lmp, bound, textrel, ret));
437 }
438