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