xref: /titanic_50/usr/src/cmd/sgs/rtld/sparc/boot_elf.s (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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 (c) 1988 AT&T
24 *	  All Rights Reserved
25 *
26 *
27 *	Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28 *	Use is subject to license terms.
29 */
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include	<link.h>
33#include	"machdep.h"
34#include	"_audit.h"
35
36#if	defined(lint)
37#include	<sys/types.h>
38#include	"_rtld.h"
39#else
40#include	<sys/stack.h>
41#include	<sys/asm_linkage.h>
42
43	.file	"boot_elf.s"
44	.seg	".text"
45#endif
46
47/*
48 * We got here because the initial call to a function resolved to a procedure
49 * linkage table entry.  That entry did a branch to the first PLT entry, which
50 * in turn did a call to elf_rtbndr (refer elf_plt_init()).
51 *
52 * the code sequence that got us here was:
53 *
54 * PLT entry for foo():
55 *	sethi	(.-PLT0), %g1			! not changed by rtld
56 *	ba,a	.PLT0				! patched atomically 2nd
57 *	nop					! patched first
58 *
59 * Therefore on entry, %i7 has the address of the call, which will be added
60 * to the offset to the plt entry in %g1 to calculate the plt entry address
61 * we must also subtract 4 because the address of PLT0 points to the
62 * save instruction before the call.
63 *
64 * the plt entry is rewritten:
65 *
66 * PLT entry for foo():
67 *	sethi	(.-PLT0), %g1
68 *	sethi	%hi(entry_pt), %g1
69 *	jmpl	%g1 + %lo(entry_pt), %g0
70 */
71
72#if	defined(lint)
73
74extern unsigned long	elf_bndr(Rt_map *, unsigned long, caddr_t);
75
76static void
77elf_rtbndr(Rt_map *lmp, unsigned long pltoff, caddr_t from)
78{
79	(void) elf_bndr(lmp, pltoff, from);
80}
81
82
83#else
84	.weak	_elf_rtbndr		! keep dbx happy as it likes to
85	_elf_rtbndr = elf_rtbndr	! rummage around for our symbols
86
87	.global	elf_rtbndr
88	.type   elf_rtbndr, #function
89	.align	4
90
91elf_rtbndr:
92	mov	%i7, %o0		! Save callers address(profiling)
93	save	%sp, -SA(MINFRAME), %sp	! Make a frame
94	srl	%g1, 10, %o1		! shift offset set by sethi
95					! %o1 has offset from jump slot
96					! to PLT0 which will be used to
97					! calculate plt relocation entry
98					! by elf_bndr
99	ld	[%i7 + 8], %o0		! %o0 has ptr to lm
100	call	elf_bndr		! returns function address in %o0
101	mov	%i0, %o2		! Callers address is arg 3
102	mov	%o0, %g1		! save address of routine binded
103	restore				! how many restores needed ? 2
104	jmp	%g1			! jump to it
105	restore
106	.size 	elf_rtbndr, . - elf_rtbndr
107
108#endif
109
110
111#if defined(lint)
112void
113iflush_range(caddr_t addr, size_t len)
114{
115	/* LINTED */
116	uintptr_t base;
117
118	base = (uintptr_t)addr & ~7;	/* round down to 8 byte alignment */
119	len = (len + 7) & ~7;		/* round up to multiple of 8 bytes */
120	for (len -= 8; (long)len >= 0; len -= 8)
121		/* iflush(base + len) */;
122}
123#else
124	ENTRY(iflush_range)
125	add	%o1, 7, %o1
126	andn	%o0, 7, %o0
127	andn	%o1, 7, %o1
1281:	subcc	%o1, 8, %o1
129	bge,a	1b
130	iflush	%o0 + %o1
131	retl
132	nop
133	SET_SIZE(iflush_range)
134#endif
135
136/*
137 * Initialize the first plt entry so that function calls go to elf_rtbndr
138 *
139 * The first plt entry (PLT0) is:
140 *
141 *	save	%sp, -64, %sp
142 *	call	elf_rtbndr
143 *	nop
144 *	address of lm
145 */
146
147#if	defined(lint)
148
149void
150elf_plt_init(void *plt, caddr_t lmp)
151{
152	*((uint_t *)plt + 0) = (unsigned long) M_SAVESP64;
153	*((uint_t *)plt + 4) = M_CALL | (((unsigned long)elf_rtbndr -
154			((unsigned long)plt)) >> 2);
155	*((uint_t *)plt + 8) = M_NOP;
156	*((uint_t *)plt + 12) = (unsigned long) lmp;
157}
158
159#else
160	.global	elf_plt_init
161	.type	elf_plt_init, #function
162	.align	4
163
164elf_plt_init:
165	save	%sp, -SA(MINFRAME), %sp	! Make a frame
1661:
167	call	2f
168	sethi	%hi((_GLOBAL_OFFSET_TABLE_ - (1b - .))), %l7
1692:
170	sethi	%hi(M_SAVESP64), %o0	! Get save instruction
171	or	%o0, %lo(M_SAVESP64), %o0
172	or	%l7, %lo((_GLOBAL_OFFSET_TABLE_ - (1b - .))), %l7
173	st	%o0, [%i0]		! Store in plt[0]
174	iflush	%i0
175	add	%l7, %o7, %l7
176	ld	[%l7 + elf_rtbndr], %l7
177	inc	4, %i0			! Bump plt to point to plt[1]
178	sub	%l7, %i0, %o0		! Determine -pc so as to produce
179					! offset from plt[1]
180	srl	%o0, 2, %o0		! Express offset as number of words
181	sethi	%hi(M_CALL), %o4	! Get sethi instruction
182	or	%o4, %o0, %o4		! Add elf_rtbndr address
183	st	%o4, [%i0]		! Store instruction in plt
184	iflush	%i0
185	sethi	%hi(M_NOP), %o0		! Generate nop instruction
186	st	%o0, [%i0 + 4]		! Store instruction in plt[2]
187	iflush	%i0 + 4
188	st	%i1, [%i0 + 8]		! Store instruction in plt[3]
189	iflush	%i0 + 8
190	ret
191	restore
192	.size	elf_plt_init, . - elf_plt_init
193#endif
194
195#if	defined(lint)
196
197ulong_t
198elf_plt_trace()
199{
200	return (0);
201}
202#else
203	.global	elf_plt_trace
204	.type   elf_plt_trace, #function
205	.align	4
206
207/*
208 * The dyn_plt that called us has already created a stack-frame for
209 * us and placed the following entries in it:
210 *
211 *	[%fp - 0x4]	* dyndata
212 *	[%fp - 0x8]	* prev stack size
213 *
214 * dyndata currently contains:
215 *
216 *	dyndata:
217 *	0x0	uintptr_t	*reflmp
218 *	0x4	uintptr_t	*deflmp
219 *	0x8	ulong_t		symndx
220 *	0xc	ulong_t		sb_flags
221 *	0x10	Sym		symdef.st_name
222 *	0x14			symdef.st_value
223 *	0x18			symdef.st_size
224 *	0x1c			symdef.st_info
225 *	0x1d			symdef.st_other
226 *	0x1e			symdef.st_shndx
227 */
228#define	REFLMP_OFF		0x0
229#define	DEFLMP_OFF		0x4
230#define	SYMNDX_OFF		0x8
231#define	SBFLAGS_OFF		0xc
232#define	SYMDEF_OFF		0x10
233#define	SYMDEF_VALUE_OFF	0x14
234
235elf_plt_trace:
2361:	call	2f
237	sethi	%hi(_GLOBAL_OFFSET_TABLE_+(.-1b)), %l7
2382:	or	%l7, %lo(_GLOBAL_OFFSET_TABLE_+(.-1b)), %l7
239	add	%l7, %o7, %l7
240
241	ld	[%l7+audit_flags], %l3
242	ld	[%l3], %l3		! %l3 = audit_flags
243	andcc	%l3, AF_PLTENTER, %g0
244	beq	.end_pltenter
245	ld	[%fp + -0x4], %l1	! l1 = * dyndata
246	ld	[%l1 + SBFLAGS_OFF], %l2 ! l2 = sb_flags
247	andcc	%l2, LA_SYMB_NOPLTENTER, %g0
248	beq	.start_pltenter
249	ld	[%l1 + SYMDEF_VALUE_OFF], %l0	! l0 =
250						!  sym.st_value(calling address)
251	ba	.end_pltenter
252	nop
253
254	/*
255	 * save all registers into La_sparcv8_regs
256	 */
257.start_pltenter:
258	sub	%sp, 0x20, %sp		! create space for La_sparcv8_regs
259					! storage on the stack.
260
261	sub	%fp, 0x28, %o4
262
263	st	%i0, [%o4]
264	st	%i1, [%o4 + 0x4]
265	st	%i2, [%o4 + 0x8]
266	st	%i3, [%o4 + 0xc]	! because a regwindow shift has
267	st	%i4, [%o4 + 0x10]	! already occured our current %i*
268	st	%i5, [%o4 + 0x14]	! register's are the equivalent of
269	st	%i6, [%o4 + 0x18]	! the %o* registers that the final
270	st	%i7, [%o4 + 0x1c]	! procedure shall see.
271
272	ld	[%fp + -0x4], %l1	! %l1 == * dyndata
273	ld	[%l1 + REFLMP_OFF], %o0	! %o0 = reflmp
274	ld	[%l1 + DEFLMP_OFF], %o1	! %o1 = deflmp
275	add	%l1, SYMDEF_OFF, %o2	! %o2 = symp
276	ld	[%l1 + SYMNDX_OFF], %o3	! %o3 = symndx
277	call	audit_pltenter
278	add	%l1, SBFLAGS_OFF, %o5	! %o3 = * sb_flags
279
280	mov	%o0, %l0		! %l0 == calling address
281
282	add	%sp, 0x20, %sp		! cleanup La_sparcv8_regs off
283					! of the stack.
284
285.end_pltenter:
286	/*
287	 * If *no* la_pltexit() routines exist we do not need to keep the
288	 * stack frame before we call the actual routine.  Instead we jump to
289	 * it and remove our self from the stack at the same time.
290	 */
291	ld	[%l7+audit_flags], %l3
292	ld	[%l3], %l3		! %l3 = audit_flags
293	andcc	%l3, AF_PLTEXIT, %g0
294	beq	.bypass_pltexit
295	ld	[%fp + -0x4], %l1	! %l1 = * dyndata
296	ld	[%l1 + SBFLAGS_OFF], %l2 ! %l2 = sb_flags
297	andcc	%l2, LA_SYMB_NOPLTEXIT, %g0
298	bne	.bypass_pltexit
299	nop
300
301	ba	.start_pltexit
302	nop
303.bypass_pltexit:
304	jmpl	%l0, %g0
305	restore
306
307.start_pltexit:
308	/*
309	 * In order to call la_pltexit() we must duplicate the
310	 * arguments from the 'callers' stack on our stack frame.
311	 *
312	 * First we check the size of the callers stack and grow
313	 * our stack to hold any of the arguments.  That need
314	 * duplicating (these are arguments 6->N), because the
315	 * first 6 (0->5) are passed via register windows on sparc.
316	 */
317
318	/*
319	 * The first calculation is to determine how large the
320	 * argument passing area might be.  Since there is no
321	 * way to distinquish between 'argument passing' and
322	 * 'local storage' from the previous stack this amount must
323	 * cover both.
324	 */
325	ld	[%fp + -0x8], %l1	! %l1 = callers stack size
326	sub	%l1, 0x58, %l1		! %l1 = argument space on caller's
327					!	stack
328	/*
329	 * Next we compare the prev. stack size against the audit_argcnt.
330	 * We copy at most 'audit_argcnt' arguments.
331	 *
332	 * NOTE: on sparc we always copy at least six args since these
333	 *	 are in reg-windows and not on the stack.
334	 *
335	 * NOTE: Also note that we multiply (shift really) the arg count
336	 *	 by 4 which is the 'word size' to calculate the amount
337	 *	 of stack space needed.
338	 */
339	ld	[%l7 + audit_argcnt], %l2
340	ld	[%l2], %l2		! %l2 = audit_arg_count
341	cmp	%l2, 6
342	ble	.grow_stack
343	sub	%l2, 6, %l2
344	sll	%l2, 2, %l2
345	cmp	%l1, %l2
346	ble	.grow_stack
347	nop
348	mov	%l2, %l1
349.grow_stack:
350	/*
351	 * When duplicating the stack we skip the first '0x5c' bytes.
352	 * This is the space on the stack reserved for preserving
353	 * the register windows and such and do not need to be duplicated
354	 * on this new stack frame.  We start duplicating at the
355	 * portion of the stack reserved for argument's above 6.
356	 */
357	sub	%sp, %l1, %sp		! grow our stack by amount required.
358	sra	%l1, 0x2, %l1		! %l1 = %l1 / 4 (words to copy)
359	mov	0x5c, %l2		! %l2 = index into stack & frame
360
3611:
362	cmp	%l1, 0
363	ble	2f
364	nop
365	ld	[%fp + %l2], %l3	! duplicate args from previous
366	st	%l3, [%sp + %l2]	! stack onto current stack
367	add	%l2, 0x4, %l2
368	ba	1b
369	sub	%l1, 0x1, %l1
3702:
371	mov	%i0, %o0		! copy ins to outs
372	mov	%i1, %o1
373	mov	%i2, %o2
374	mov	%i3, %o3
375	mov	%i4, %o4
376	call	%l0			! call routine
377	mov	%i5, %o5
378	mov	%o1, %l2		! l2 = second 1/2 of return value
379					! for those those 64 bit operations
380					! link div64 - yuck...
381
382					! %o0 = retval
383	ld	[%fp + -0x4], %l1
384	ld	[%l1 + REFLMP_OFF], %o1	! %o1 = reflmp
385	ld	[%l1 + DEFLMP_OFF], %o2	! %o2 = deflmp
386	add	%l1, SYMDEF_OFF, %o3	! %o3 = symp
387	call	audit_pltexit
388	ld	[%l1 + SYMNDX_OFF], %o4	! %o4 = symndx
389
390	mov	%o0, %i0		! pass on return code
391	mov	%l2, %i1
392	ret
393	restore
394	.size	elf_plt_trace, . - elf_plt_trace
395
396#endif
397
398/*
399 * After the first call to a plt, elf_bndr() will have determined the true
400 * address of the function being bound.  The plt is now rewritten so that
401 * any subsequent calls go directly to the bound function.  If the library
402 * to which the function belongs is being profiled refer to _plt_cg_write.
403 *
404 * the new plt entry is:
405 *
406 *	sethi	(.-PLT0), %g1			! constant
407 *	sethi	%hi(function address), %g1	! patched second
408 *	jmpl	%g1 + %lo(function address, %g0	! patched first
409 */
410
411#if	defined(lint)
412
413void
414plt_full_range(uintptr_t pc, uintptr_t symval)
415{
416	uint_t *	plttab = (uint_t *)pc;
417	plttab[2] = (M_JMPL | ((unsigned long)symval & S_MASK(10)));
418	plttab[1] = (M_SETHIG1 | ((unsigned long)symval >> (32 - 22)));
419}
420
421#else
422	ENTRY(plt_full_range)
423
424	sethi	%hi(M_JMPL), %o3	! Get jmpl instruction
425	and	%o1, 0x3ff, %o2		! Lower part of function address
426	or	%o3, %o2, %o3		!	is or'ed into instruction
427	st	%o3, [%o0 + 8]		! Store instruction in plt[2]
428	iflush	%o0 + 8
429	stbar
430
431	srl	%o1, 10, %o1		! Get high part of function address
432	sethi	%hi(M_SETHIG1), %o3	! Get sethi instruction
433	or	%o3, %o1, %o3		! Add sethi and function address
434	st	%o3, [%o0 + 4]		! Store instruction in plt[1]
435	retl
436	iflush	%o0 + 4
437
438	SET_SIZE(plt_full_range)
439
440#endif	/* defined(lint) */
441
442