xref: /titanic_44/usr/src/cmd/sgs/rtld/sparc/sparc_a.out.c (revision eb9dbf0cbc8141f9da4ed18e6b5515d9eaea0e00)
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 2010 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	"_inline.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 	Sresult		sr;
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 = NEXT_RT_MAP(lmp)) {
90 		if (THIS_IS_AOUT(lmp)) {
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 	SRESULT_INIT(sr, name);
121 
122 	if (aout_lookup_sym(&sl, &sr, &binfo, NULL) == 0) {
123 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
124 		    demangle(name));
125 		rtldexit(lml, 1);
126 	}
127 
128 	name = (char *)sr.sr_name;
129 	nlmp = sr.sr_dmap;
130 	sym = sr.sr_sym;
131 
132 	symval = sym->st_value;
133 
134 	if (!(FLAGS(nlmp) & FLG_RT_FIXED) &&
135 	    (sym->st_shndx != SHN_ABS))
136 		symval += (int)(ADDR(nlmp));
137 	if ((lmp != nlmp) && ((FLAGS1(nlmp) & FL1_RT_NOINIFIN) == 0)) {
138 		/*
139 		 * Record that this new link map is now bound to the caller.
140 		 */
141 		if (bind_one(lmp, nlmp, BND_REFER) == 0)
142 			rtldexit(lml, 1);
143 	}
144 
145 	/*
146 	 * Print binding information and rebuild PLT entry.
147 	 */
148 	DBG_CALL(Dbg_bind_global(lmp, (Addr)(ADDR(lmp) + rp->r_address),
149 	    (Off)rp->r_address, (Xword)(-1), PLT_T_NONE, nlmp,
150 	    (Addr)symval, sym->st_value, name, binfo));
151 
152 	if (!(rtld_flags & RT_FL_NOBIND))
153 		aout_plt_write((caddr_t)(ADDR(lmp) + rp->r_address), symval);
154 
155 	/*
156 	 * Complete any processing for newly loaded objects.  Note we don't
157 	 * know exactly where any new objects are loaded (we know the object
158 	 * that supplied the symbol, but others may have been loaded lazily as
159 	 * we searched for the symbol), so sorting starts from the last
160 	 * link-map know on entry to this routine.
161 	 */
162 	if (entry)
163 		load_completion(llmp);
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, APlist **textrel)
209 {
210 	int		k;		/* loop temporary */
211 	int		nr;		/* number of relocations */
212 	char		*name;		/* symbol being searched for */
213 	long		value;		/* relocation temporary */
214 	long		*ra;		/* cached relocation address */
215 	struct relocation_info *rp;	/* current relocation */
216 	struct nlist	*sp;		/* symbol table of "symbol" */
217 	Rt_map *	_lmp;		/* lm which holds symbol definition */
218 	Sym *		sym;		/* symbol definition */
219 	int		ret = 1;
220 	APlist		*bound = NULL;
221 	Lm_list		*lml = LIST(lmp);
222 
223 	DBG_CALL(Dbg_reloc_run(lmp, SHT_RELA, plt, DBG_REL_START));
224 
225 	/*
226 	 * If we've been called upon to promote an RTLD_LAZY object to an
227 	 * RTLD_NOW don't bother to do anything - a.out's are bound as if
228 	 * RTLD_NOW regardless.
229 	 */
230 	if (plt)
231 		return (1);
232 
233 	rp = LM2LP(lmp)->lp_rp;
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 		mmapobj_result_t	*mpp;
248 
249 		/* LINTED */
250 		ra = (long *)&((char *)ADDR(lmp))[rp->r_address];
251 
252 		/*
253 		 * Make sure the segment is writable.
254 		 */
255 		if (((mpp = find_segment((caddr_t)ra, lmp)) != NULL) &&
256 		    ((mpp->mr_prot & PROT_WRITE) == 0)) {
257 			if ((set_prot(lmp, mpp, 1) == 0) ||
258 			    (aplist_append(textrel, mpp,
259 			    AL_CNT_TEXTREL) == NULL)) {
260 				ret = 0;
261 				break;
262 			}
263 		}
264 
265 		/*
266 		 * Perform the relocation.
267 		 */
268 		if (rp->r_extern == 0) {
269 			name = NULL;
270 			value = ADDR(lmp);
271 		} else {
272 			Slookup		sl;
273 			Sresult		sr;
274 			uint_t		binfo;
275 
276 			if (rp->r_type == RELOC_JMP_SLOT)
277 				continue;
278 			sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
279 			name = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
280 
281 			/*
282 			 * Locate symbol.  Initialize the symbol lookup data
283 			 * structure.
284 			 */
285 			SLOOKUP_INIT(sl, name, lmp, 0, ld_entry_cnt,
286 			    0, 0, 0, 0, LKUP_STDRELOC);
287 			SRESULT_INIT(sr, name);
288 
289 			if (aout_lookup_sym(&sl, &sr, &binfo, in_nfavl) == 0) {
290 				if (lml->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(lml, 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 			name = (char *)sr.sr_name;
309 			_lmp = sr.sr_dmap;
310 			sym = sr.sr_sym;
311 
312 			if ((lmp != _lmp) &&
313 			    ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) {
314 				if (aplist_test(&bound, _lmp,
315 				    AL_CNT_RELBIND) == 0) {
316 					ret = 0;
317 					break;
318 				}
319 			}
320 
321 			value = sym->st_value + rp->r_addend;
322 			if (!(FLAGS(_lmp) & FLG_RT_FIXED) &&
323 			    (sym->st_shndx != SHN_COMMON) &&
324 			    (sym->st_shndx != SHN_ABS))
325 				value += ADDR(_lmp);
326 
327 			if (IS_PC_RELATIVE(rp->r_type))
328 				value -= (long)ADDR(lmp);
329 
330 			DBG_CALL(Dbg_bind_global(lmp, (Addr)ra,
331 			    (Off)(ra - ADDR(lmp)), (Xword)(-1), PLT_T_NONE,
332 			    _lmp, (Addr)value, sym->st_value, name, binfo));
333 		}
334 
335 		/*
336 		 * Perform a specific relocation operation.
337 		 */
338 		switch (rp->r_type) {
339 		case RELOC_RELATIVE:
340 			value += *ra << (32-22);
341 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
342 			    ((value >> (32 - 22)) & S_MASK(22));
343 			ra++;
344 			value += (*ra & S_MASK(10));
345 			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
346 			    (value & S_MASK(10));
347 			break;
348 		case RELOC_8:
349 		case RELOC_DISP8:
350 			value += *ra & S_MASK(8);
351 			if (!S_INRANGE(value, 8)) {
352 				eprintf(lml, ERR_FATAL,
353 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
354 				    (name ? demangle(name) :
355 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 8,
356 				    (uint_t)ra);
357 			}
358 			*ra = value;
359 			break;
360 		case RELOC_LO10:
361 		case RELOC_BASE10:
362 			value += *ra & S_MASK(10);
363 			*(long *)ra = (*(long *)ra & ~S_MASK(10)) |
364 			    (value & S_MASK(10));
365 			break;
366 		case RELOC_BASE13:
367 		case RELOC_13:
368 			value += *ra & S_MASK(13);
369 			*(long *)ra = (*(long *)ra & ~S_MASK(13)) |
370 			    (value & S_MASK(13));
371 			break;
372 		case RELOC_16:
373 		case RELOC_DISP16:
374 			value += *ra & S_MASK(16);
375 			if (!S_INRANGE(value, 16)) {
376 				eprintf(lml, ERR_FATAL,
377 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
378 				    (name ? demangle(name) :
379 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 16,
380 				    (uint_t)ra);
381 			}
382 			*(short *)ra = value;
383 			break;
384 		case RELOC_22:
385 		case RELOC_BASE22:
386 			value += *ra & S_MASK(22);
387 			if (!S_INRANGE(value, 22)) {
388 				eprintf(lml, ERR_FATAL,
389 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
390 				    (name ? demangle(name) :
391 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
392 				    (uint_t)ra);
393 			}
394 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
395 			    (value & S_MASK(22));
396 			break;
397 		case RELOC_HI22:
398 			value += (*ra & S_MASK(22)) << (32 - 22);
399 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
400 			    ((value >> (32 - 22)) & S_MASK(22));
401 			break;
402 		case RELOC_WDISP22:
403 			value += *ra & S_MASK(22);
404 			value >>= 2;
405 			if (!S_INRANGE(value, 22)) {
406 				eprintf(lml, ERR_FATAL,
407 				    MSG_INTL(MSG_REL_OVERFLOW), NAME(lmp),
408 				    (name ? demangle(name) :
409 				    MSG_INTL(MSG_STR_UNKNOWN)), (int)value, 22,
410 				    (uint_t)ra);
411 			}
412 			*(long *)ra = (*(long *)ra & ~S_MASK(22)) |
413 			    (value & S_MASK(22));
414 			break;
415 		case RELOC_WDISP30:
416 			value += *ra & S_MASK(30);
417 			value >>= 2;
418 			*(long *)ra = (*(long *)ra & ~S_MASK(30)) |
419 			    (value & S_MASK(30));
420 			break;
421 		case RELOC_32:
422 		case RELOC_GLOB_DAT:
423 		case RELOC_DISP32:
424 			value += *ra;
425 			*(long *)ra = value;
426 			break;
427 		default:
428 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_UNIMPL),
429 			    NAME(lmp), (name ? demangle(name) :
430 			    MSG_INTL(MSG_STR_UNKNOWN)), rp->r_type);
431 			ret = 0;
432 			break;
433 		}
434 
435 		/*
436 		 * If this relocation is against a text segment we must make
437 		 * sure that the instruction cache is flushed.
438 		 */
439 		if (textrel) {
440 			if (rp->r_type == RELOC_RELATIVE)
441 				iflush_range((caddr_t)(ra - 1), 0x8);
442 			else
443 				iflush_range((caddr_t)ra, 0x4);
444 		}
445 	}
446 
447 	return (relocate_finish(lmp, bound, ret));
448 }
449