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