xref: /titanic_44/usr/src/cmd/sgs/rtld/amd64/boot_elf.s (revision d29f5a711240f866521445b1656d114da090335e)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#if	defined(lint)
30
31#include	<sys/types.h>
32#include	<_rtld.h>
33#include	<_audit.h>
34#include	<_elf.h>
35#include	<sys/regset.h>
36
37/* ARGSUSED0 */
38int
39elf_plt_trace()
40{
41	return (0);
42}
43#else
44
45#include	<link.h>
46#include	<_audit.h>
47#include	<sys/asm_linkage.h>
48
49	.file	"boot_elf.s"
50	.text
51
52/*
53 * On entry the 'glue code' has already  done the following:
54 *
55 *	pushq	%rbp
56 *	movq	%rsp, %rbp
57 *	subq	$0x10, %rsp
58 *	leaq	trace_fields(%rip), %r11
59 *	movq	%r11, -0x8(%rbp)
60 *	movq	$elf_plt_trace, %r11
61 *	jmp	*%r11
62 *
63 * so - -8(%rbp) contains the dyndata ptr
64 *
65 *	0x0	Addr		*reflmp
66 *	0x8	Addr		*deflmp
67 *	0x10	Word		symndx
68 *	0x14	Word		sb_flags
69 *	0x18	Sym		symdef.st_name
70 *	0x1c			symdef.st_info
71 *	0x1d			symdef.st_other
72 *	0x1e			symdef.st_shndx
73 *	0x20			symdef.st_value
74 *	0x28			symdef.st_size
75 *
76 * Also note - on entry 16 bytes have already been subtracted
77 * from the %rsp.  The first 8 bytes is for the dyn_data_ptr,
78 * the second 8 bytes are to align the stack and are available
79 * for use.
80 */
81#define	REFLMP_OFF		0x0
82#define	DEFLMP_OFF		0x8
83#define	SYMNDX_OFF		0x10
84#define	SBFLAGS_OFF		0x14
85#define	SYMDEF_OFF		0x18
86#define	SYMDEF_VALUE_OFF	0x20
87/*
88 * Local stack space storage for elf_plt_trace is allocated
89 * as follows:
90 *
91 *  First - before we got here - %rsp has been decremented
92 *  by 0x10 to make space for the dyndata ptr (and another
93 *  free word).  In addition to that, we create space
94 *  for the following:
95 *
96 *	La_amd64_regs	    8 * 8:	64
97 *	prev_stack_size	    8		 8
98 *	Saved regs:
99 *	    %rdi			 8
100 *	    %rsi			 8
101 *	    %rdx			 8
102 *	    %rcx			 8
103 *	    %r8				 8
104 *	    %r9				 8
105 *	    %r10			 8
106 *	    %r11			 8
107 *	    %rax			 8
108 *				    =======
109 *			    Subtotal:	144 (16byte aligned)
110 *
111 *	Saved Media Regs (used to pass floating point args):
112 *	    %xmm0 - %xmm7   16 * 8:	128
113 *				    =======
114 *			    Total:	272 (16byte aligned)
115 *
116 *  So - will subtract the following to create enough space
117 *
118 *	-8(%rbp)	store dyndata ptr
119 *	-16(%rbp)	store call destination
120 *	-80(%rbp)	space for La_amd64_regs
121 *	-88(%rbp)	prev stack size
122 *  The next %rbp offsets are only true if the caller had correct stack
123 *  alignment.  See note above SPRDIOFF for why we use %rsp alignment to
124 *  access these stack fields.
125 *	-96(%rbp)	entering %rdi
126 *	-104(%rbp)	entering %rsi
127 *	-112(%rbp)	entering %rdx
128 *	-120(%rbp)	entering %rcx
129 *	-128(%rbp)	entering %r8
130 *	-136(%rbp)	entering %r9
131 *	-144(%rbp)	entering %r10
132 *	-152(%rbp)	entering %r11
133 *	-160(%rbp)	entering %rax
134 *	-176(%rbp)	entering %xmm0
135 *	-192(%rbp)	entering %xmm1
136 *	-208(%rbp)	entering %xmm2
137 *	-224(%rbp)	entering %xmm3
138 *	-240(%rbp)	entering %xmm4
139 *	-256(%rbp)	entering %xmm5
140 *	-272(%rbp)	entering %xmm6
141 *	-288(%rbp)	entering %xmm7
142 *
143 */
144#define	SPDYNOFF    -8
145#define	SPDESTOFF   -16
146#define	SPLAREGOFF  -80
147#define	SPPRVSTKOFF -88
148
149/*
150 * The next set of offsets are relative to %rsp.
151 * We guarantee %rsp is ABI compliant 16-byte aligned.  This guarantees the
152 * xmm registers are saved to 16-byte aligned addresses.
153 * %rbp may only be 8 byte aligned if we came in from non-ABI compliant code.
154 */
155#define	SPRDIOFF	192
156#define	SPRSIOFF	184
157#define	SPRDXOFF	176
158#define	SPRCXOFF	168
159#define	SPR8OFF		160
160#define	SPR9OFF		152
161#define	SPR10OFF	144
162#define	SPR11OFF	136
163#define	SPRAXOFF	128
164#define	SPXMM0OFF	112
165#define	SPXMM1OFF	96
166#define	SPXMM2OFF	80
167#define	SPXMM3OFF	64
168#define	SPXMM4OFF	48
169#define	SPXMM5OFF	32
170#define	SPXMM6OFF	16
171#define	SPXMM7OFF	0
172
173	.globl	elf_plt_trace
174	.type	elf_plt_trace,@function
175	.align 16
176elf_plt_trace:
177	/*
178	 * Enforce ABI 16-byte stack alignment here.
179	 * The next andq instruction does this pseudo code:
180	 * If %rsp is 8 byte aligned then subtract 8 from %rsp.
181	 */
182	andq    $-16, %rsp	/* enforce ABI 16-byte stack alignment */
183	subq	$272,%rsp	/ create some local storage
184
185	movq	%rdi, SPRDIOFF(%rsp)
186	movq	%rsi, SPRSIOFF(%rsp)
187	movq	%rdx, SPRDXOFF(%rsp)
188	movq	%rcx, SPRCXOFF(%rsp)
189	movq	%r8, SPR8OFF(%rsp)
190	movq	%r9, SPR9OFF(%rsp)
191	movq	%r10, SPR10OFF(%rsp)
192	movq	%r11, SPR11OFF(%rsp)
193	movq	%rax, SPRAXOFF(%rsp)
194	movdqa	%xmm0, SPXMM0OFF(%rsp)
195	movdqa	%xmm1, SPXMM1OFF(%rsp)
196	movdqa	%xmm2, SPXMM2OFF(%rsp)
197	movdqa	%xmm3, SPXMM3OFF(%rsp)
198	movdqa	%xmm4, SPXMM4OFF(%rsp)
199	movdqa	%xmm5, SPXMM5OFF(%rsp)
200	movdqa	%xmm6, SPXMM6OFF(%rsp)
201	movdqa	%xmm7, SPXMM7OFF(%rsp)
202
203	movq	SPDYNOFF(%rbp), %rax			/ %rax = dyndata
204	testb	$LA_SYMB_NOPLTENTER, SBFLAGS_OFF(%rax)	/ <link.h>
205	je	.start_pltenter
206	movq	SYMDEF_VALUE_OFF(%rax), %rdi
207	movq	%rdi, SPDESTOFF(%rbp)		/ save destination address
208	jmp	.end_pltenter
209
210.start_pltenter:
211	/*
212	 * save all registers into La_amd64_regs
213	 */
214	leaq	SPLAREGOFF(%rbp), %rsi	/ %rsi = &La_amd64_regs
215	leaq	8(%rbp), %rdi
216	movq	%rdi, 0(%rsi)		/ la_rsp
217	movq	0(%rbp), %rdi
218	movq	%rdi, 8(%rsi)		/ la_rbp
219	movq	SPRDIOFF(%rsp), %rdi
220	movq	%rdi, 16(%rsi)		/ la_rdi
221	movq	SPRSIOFF(%rsp), %rdi
222	movq	%rdi, 24(%rsi)		/ la_rsi
223	movq	SPRDXOFF(%rsp), %rdi
224	movq	%rdi, 32(%rsi)		/ la_rdx
225	movq	SPRCXOFF(%rsp), %rdi
226	movq	%rdi, 40(%rsi)		/ la_rcx
227	movq	SPR8OFF(%rsp), %rdi
228	movq	%rdi, 48(%rsi)		/ la_r8
229	movq	SPR9OFF(%rsp), %rdi
230	movq	%rdi, 56(%rsi)		/ la_r9
231
232	/*
233	 * prepare for call to la_pltenter
234	 */
235	movq	SPDYNOFF(%rbp), %r11		/ %r11 = &dyndata
236	leaq	SBFLAGS_OFF(%r11), %r9		/ arg6 (&sb_flags)
237	leaq	SPLAREGOFF(%rbp), %r8		/ arg5 (&La_amd64_regs)
238	movl	SYMNDX_OFF(%r11), %ecx		/ arg4 (symndx)
239	leaq	SYMDEF_OFF(%r11), %rdx		/ arg3 (&Sym)
240	movq	DEFLMP_OFF(%r11), %rsi		/ arg2 (dlmp)
241	movq	REFLMP_OFF(%r11), %rdi		/ arg1 (rlmp)
242	call	audit_pltenter@PLT
243	movq	%rax, SPDESTOFF(%rbp)		/ save calling address
244.end_pltenter:
245
246	/*
247	 * If *no* la_pltexit() routines exist
248	 * we do not need to keep the stack frame
249	 * before we call the actual routine.  Instead we
250	 * jump to it and remove our stack from the stack
251	 * at the same time.
252	 */
253	movl	audit_flags(%rip), %eax
254	andl	$AF_PLTEXIT, %eax		/ value of audit.h:AF_PLTEXIT
255	cmpl	$0, %eax
256	je	.bypass_pltexit
257	/*
258	 * Has the *nopltexit* flag been set for this entry point
259	 */
260	movq	SPDYNOFF(%rbp), %r11		/ %r11 = &dyndata
261	testb	$LA_SYMB_NOPLTEXIT, SBFLAGS_OFF(%r11)
262	je	.start_pltexit
263
264.bypass_pltexit:
265	/*
266	 * No PLTEXIT processing required.
267	 */
268	movq	0(%rbp), %r11
269	movq	%r11, -8(%rbp)			/ move prev %rbp
270	movq	SPDESTOFF(%rbp), %r11		/ r11 == calling destination
271	movq	%r11, 0(%rbp)			/ store destination at top
272
273	/
274	/ Restore registers
275	/
276	movq	SPRDIOFF(%rsp), %rdi
277	movq	SPRSIOFF(%rsp), %rsi
278	movq	SPRDXOFF(%rsp), %rdx
279	movq	SPRCXOFF(%rsp), %rcx
280	movq	SPR8OFF(%rsp), %r8
281	movq	SPR9OFF(%rsp), %r9
282	movq	SPR10OFF(%rsp), %r10
283	movq	SPR11OFF(%rsp), %r11
284	movq	SPRAXOFF(%rsp), %rax
285	movdqa	SPXMM0OFF(%rsp), %xmm0
286	movdqa	SPXMM1OFF(%rsp), %xmm1
287	movdqa	SPXMM2OFF(%rsp), %xmm2
288	movdqa	SPXMM3OFF(%rsp), %xmm3
289	movdqa	SPXMM4OFF(%rsp), %xmm4
290	movdqa	SPXMM5OFF(%rsp), %xmm5
291	movdqa	SPXMM6OFF(%rsp), %xmm6
292	movdqa	SPXMM7OFF(%rsp), %xmm7
293
294	subq	$8, %rbp			/ adjust %rbp for 'ret'
295	movq	%rbp, %rsp			/
296	/*
297	 * At this point, after a little doctoring, we should
298	 * have the following on the stack:
299	 *
300	 *	16(%rsp):  ret addr
301	 *	8(%rsp):  dest_addr
302	 *	0(%rsp):  Previous %rbp
303	 *
304	 * So - we pop the previous %rbp, and then
305	 * ret to our final destination.
306	 */
307	popq	%rbp				/
308	ret					/ jmp to final destination
309						/ and clean up stack :)
310
311.start_pltexit:
312	/*
313	 * In order to call the destination procedure and then return
314	 * to audit_pltexit() for post analysis we must first grow
315	 * our stack frame and then duplicate the original callers
316	 * stack state.  This duplicates all of the arguements
317	 * that were to be passed to the destination procedure.
318	 */
319	movq	%rbp, %rdi			/
320	addq	$16, %rdi			/    %rdi = src
321	movq	(%rbp), %rdx			/
322	subq	%rdi, %rdx			/    %rdx == prev frame sz
323	/*
324	 * If audit_argcnt > 0 then we limit the number of
325	 * arguements that will be duplicated to audit_argcnt.
326	 *
327	 * If (prev_stack_size > (audit_argcnt * 8))
328	 *	prev_stack_size = audit_argcnt * 8;
329	 */
330	movl	audit_argcnt(%rip),%eax		/   %eax = audit_argcnt
331	cmpl	$0, %eax
332	jle	.grow_stack
333	leaq	(,%rax,8), %rax			/    %eax = %eax * 4
334	cmpq	%rax,%rdx
335	jle	.grow_stack
336	movq	%rax, %rdx
337	/*
338	 * Grow the stack and duplicate the arguements of the
339	 * original caller.
340	 *
341	 * We save %rsp in %r11 since we need to use the current rsp for
342	 * accessing the registers saved in our stack frame.
343	 */
344.grow_stack:
345	movq	%rsp, %r11
346	subq	%rdx, %rsp			/    grow the stack
347	movq	%rdx, SPPRVSTKOFF(%rbp)		/    -88(%rbp) == prev frame sz
348	movq	%rsp, %rcx			/    %rcx = dest
349	addq	%rcx, %rdx			/    %rdx == tail of dest
350.while_base:
351	cmpq	%rdx, %rcx			/   while (base+size >= src++) {
352	jge	.end_while			/
353	movq	(%rdi), %rsi
354	movq	%rsi,(%rcx)			/        *dest = *src
355	addq	$8, %rdi			/	 src++
356	addq	$8, %rcx			/        dest++
357	jmp	.while_base			/    }
358
359	/*
360	 * The above stack is now an exact duplicate of
361	 * the stack of the original calling procedure.
362	 */
363.end_while:
364	/
365	/ Restore registers using %r11 which contains our old %rsp value
366	/ before growing the stack.
367	/
368	movq	SPRDIOFF(%r11), %rdi
369	movq	SPRSIOFF(%r11), %rsi
370	movq	SPRDXOFF(%r11), %rdx
371	movq	SPRCXOFF(%r11), %rcx
372	movq	SPR8OFF(%r11), %r8
373	movq	SPR9OFF(%r11), %r9
374	movq	SPR10OFF(%r11), %r10
375	movq	SPRAXOFF(%r11), %rax
376	movdqa	SPXMM0OFF(%r11), %xmm0
377	movdqa	SPXMM1OFF(%r11), %xmm1
378	movdqa	SPXMM2OFF(%r11), %xmm2
379	movdqa	SPXMM3OFF(%r11), %xmm3
380	movdqa	SPXMM4OFF(%r11), %xmm4
381	movdqa	SPXMM5OFF(%r11), %xmm5
382	movdqa	SPXMM6OFF(%r11), %xmm6
383	movdqa	SPXMM7OFF(%r11), %xmm7
384	movq	SPR11OFF(%r11), %r11		/ retore %r11 last
385
386	/*
387	 * Call to desitnation function - we'll return here
388	 * for pltexit monitoring.
389	 */
390	call	*SPDESTOFF(%rbp)
391
392	addq	SPPRVSTKOFF(%rbp), %rsp	/ cleanup dupped stack
393
394	/
395	/ prepare for call to audit_pltenter()
396	/
397	movq	SPDYNOFF(%rbp), %r11		/ %r11 = &dyndata
398	movq	SYMNDX_OFF(%r11), %r8		/ arg5 (symndx)
399	leaq	SYMDEF_OFF(%r11), %rcx		/ arg4 (&Sym)
400	movq	DEFLMP_OFF(%r11), %rdx		/ arg3 (dlmp)
401	movq	REFLMP_OFF(%r11), %rsi		/ arg2 (rlmp)
402	movq	%rax, %rdi			/ arg1 (returnval)
403	call	audit_pltexit@PLT
404
405	/*
406	 * Clean up after ourselves and return to the
407	 * original calling procedure.
408	 */
409
410	/
411	/ Restore registers
412	/
413	movq	SPRDIOFF(%rsp), %rdi
414	movq	SPRSIOFF(%rsp), %rsi
415	movq	SPRDXOFF(%rsp), %rdx
416	movq	SPRCXOFF(%rsp), %rcx
417	movq	SPR8OFF(%rsp), %r8
418	movq	SPR9OFF(%rsp), %r9
419	movq	SPR10OFF(%rsp), %r10
420	movq	SPR11OFF(%rsp), %r11
421	// rax already contains return value
422	movdqa	SPXMM0OFF(%rsp), %xmm0
423	movdqa	SPXMM1OFF(%rsp), %xmm1
424	movdqa	SPXMM2OFF(%rsp), %xmm2
425	movdqa	SPXMM3OFF(%rsp), %xmm3
426	movdqa	SPXMM4OFF(%rsp), %xmm4
427	movdqa	SPXMM5OFF(%rsp), %xmm5
428	movdqa	SPXMM6OFF(%rsp), %xmm6
429	movdqa	SPXMM7OFF(%rsp), %xmm7
430
431	movq	%rbp, %rsp			/
432	popq	%rbp				/
433	ret					/ return to caller
434	.size	elf_plt_trace, .-elf_plt_trace
435#endif
436
437/*
438 * We got here because a call to a function resolved to a procedure
439 * linkage table entry.  That entry did a JMPL to the first PLT entry, which
440 * in turn did a call to elf_rtbndr.
441 *
442 * the code sequence that got us here was:
443 *
444 * .PLT0:
445 *	pushq	GOT+8(%rip)	#GOT[1]
446 *	jmp	*GOT+16(%rip)	#GOT[2]
447 *	nop
448 *	nop
449 *	nop
450 *	nop
451 *	...
452 * PLT entry for foo:
453 *	jmp	*name1@GOTPCREL(%rip)
454 *	pushl	$rel.plt.foo
455 *	jmp	PLT0
456 *
457 * At entry, the stack looks like this:
458 *
459 *	return address			16(%rsp)
460 *	$rel.plt.foo	(plt index)	8(%rsp)
461 *	lmp				0(%rsp)
462 *
463 */
464#if defined(lint)
465
466extern unsigned long	elf_bndr(Rt_map *, unsigned long, caddr_t);
467
468void
469elf_rtbndr(Rt_map * lmp, unsigned long reloc, caddr_t pc)
470{
471	(void) elf_bndr(lmp, reloc, pc);
472}
473
474#else
475
476/*
477 * The PLT code that landed us here placed 2 arguments on the stack as
478 * arguments to elf_rtbndr.
479 * Additionally the pc of caller is below these 2 args.
480 * Our stack will look like this after we establish a stack frame with
481 * push %rbp; movq %rsp, %rbp sequence:
482 *
483 *	8(%rbp)			arg1 - *lmp
484 *	16(%rbp), %rsi		arg2 - reloc index
485 *	24(%rbp), %rdx		arg3 - pc of caller
486 */
487#define	LBPLMPOFF	8	/* arg1 - *lmp */
488#define	LBPRELOCOFF	16	/* arg2 - reloc index */
489#define	LBRPCOFF	24	/* arg3 - pc of caller */
490
491/*
492 * Possible arguments for the resolved function are in registers as per
493 * the AMD64 ABI.  We must save on the local stack all possible register
494 * arguments before interposing functions to resolve the called function.
495 * Possible arguments must be restored before invoking the resolved function.
496 *
497 * Local stack space storage for elf_rtbndr is allocated as follows:
498 *
499 *	Saved regs:
500 *	    %rax			 8
501 *	    %rdi			 8
502 *	    %rsi			 8
503 *	    %rdx			 8
504 *	    %rcx			 8
505 *	    %r8				 8
506 *	    %r9				 8
507 *	    %r10			 8
508 *				    =======
509 *			    Subtotal:   64 (16byte aligned)
510 *
511 *	Saved Media Regs (used to pass floating point args):
512 *	    %xmm0 - %xmm7   16 * 8:    128
513 *				    =======
514 *			    Total:     192 (16byte aligned)
515 *
516 *  So - will subtract the following to create enough space
517 *
518 *	0(%rsp)		save %rax
519 *	8(%rsp)		save %rdi
520 *	16(%rsp)	save %rsi
521 *	24(%rsp)	save %rdx
522 *	32(%rsp)	save %rcx
523 *	40(%rsp)	save %r8
524 *	48(%rsp)	save %r9
525 *	56(%rsp)	save %r10
526 *	64(%rsp)	save %xmm0
527 *	80(%rsp)	save %xmm1
528 *	96(%rsp)	save %xmm2
529 *	112(%rsp)	save %xmm3
530 *	128(%rsp)	save %xmm4
531 *	144(%rsp)	save %xmm5
532 *	160(%rsp)	save %xmm6
533 *	176(%rsp)	save %xmm7
534 *
535 * Note: Some callers may use 8-byte stack alignment instead of the
536 * ABI required 16-byte alignment.  We use %rsp offsets to save/restore
537 * registers because %rbp may not be 16-byte aligned.  We guarantee %rsp
538 * is 16-byte aligned in the function preamble.
539 */
540#define	LS_SIZE	$192	/* local stack space to save all possible arguments */
541#define	LSRAXOFF	0	/* for SSE register count */
542#define	LSRDIOFF	8	/* arg 0 ... */
543#define	LSRSIOFF	16
544#define	LSRDXOFF	24
545#define	LSRCXOFF	32
546#define	LSR8OFF		40
547#define	LSR9OFF		48
548#define	LSR10OFF	56	/* ... arg 5 */
549#define	LSXMM0OFF	64	/* SSE arg 0 ... */
550#define	LSXMM1OFF	80
551#define	LSXMM2OFF	96
552#define	LSXMM3OFF	112
553#define	LSXMM4OFF	128
554#define	LSXMM5OFF	144
555#define	LSXMM6OFF	160
556#define	LSXMM7OFF	176	/* ... SSE arg 7 */
557
558	.weak	_elf_rtbndr
559	_elf_rtbndr = elf_rtbndr
560
561	ENTRY(elf_rtbndr)
562
563	pushq	%rbp
564	movq	%rsp, %rbp
565
566	/*
567	 * Some libraries may (incorrectly) use non-ABI compliant 8-byte stack
568	 * alignment.  Enforce ABI 16-byte stack alignment here.
569	 * The next andq instruction does this pseudo code:
570	 * If %rsp is 8 byte aligned then subtract 8 from %rsp.
571	 */
572	andq	$-16, %rsp	/* enforce ABI 16-byte stack alignment */
573
574	subq	LS_SIZE, %rsp	/* save all ABI defined argument registers */
575
576	movq	%rax, LSRAXOFF(%rsp)	/* for SSE register count */
577	movq	%rdi, LSRDIOFF(%rsp)	/*  arg 0 .. */
578	movq	%rsi, LSRSIOFF(%rsp)
579	movq	%rdx, LSRDXOFF(%rsp)
580	movq	%rcx, LSRCXOFF(%rsp)
581	movq	%r8, LSR8OFF(%rsp)
582	movq	%r9, LSR9OFF(%rsp)	/* .. arg 5 */
583	movq	%r10, LSR10OFF(%rsp)	/* call chain reg */
584
585	movdqa	%xmm0, LSXMM0OFF(%rsp)	/* SSE arg 0 ... */
586	movdqa	%xmm1, LSXMM1OFF(%rsp)
587	movdqa	%xmm2, LSXMM2OFF(%rsp)
588	movdqa	%xmm3, LSXMM3OFF(%rsp)
589	movdqa	%xmm4, LSXMM4OFF(%rsp)
590	movdqa	%xmm5, LSXMM5OFF(%rsp)
591	movdqa	%xmm6, LSXMM6OFF(%rsp)
592	movdqa	%xmm7, LSXMM7OFF(%rsp)	/* ... SSE arg 7 */
593
594	movq	LBPLMPOFF(%rbp), %rdi	/* arg1 - *lmp */
595	movq	LBPRELOCOFF(%rbp), %rsi	/* arg2 - reloc index */
596	movq	LBRPCOFF(%rbp), %rdx	/* arg3 - pc of caller */
597	call	elf_bndr@PLT		/* call elf_rtbndr(lmp, relndx, pc) */
598	movq	%rax, LBPRELOCOFF(%rbp)	/* store final destination */
599
600	/* restore possible arguments before invoking resolved function */
601	movq	LSRAXOFF(%rsp), %rax
602	movq	LSRDIOFF(%rsp), %rdi
603	movq	LSRSIOFF(%rsp), %rsi
604	movq	LSRDXOFF(%rsp), %rdx
605	movq	LSRCXOFF(%rsp), %rcx
606	movq	LSR8OFF(%rsp), %r8
607	movq	LSR9OFF(%rsp), %r9
608	movq	LSR10OFF(%rsp), %r10
609
610	movdqa	LSXMM0OFF(%rsp), %xmm0
611	movdqa	LSXMM1OFF(%rsp), %xmm1
612	movdqa	LSXMM2OFF(%rsp), %xmm2
613	movdqa	LSXMM3OFF(%rsp), %xmm3
614	movdqa	LSXMM4OFF(%rsp), %xmm4
615	movdqa	LSXMM5OFF(%rsp), %xmm5
616	movdqa	LSXMM6OFF(%rsp), %xmm6
617	movdqa	LSXMM7OFF(%rsp), %xmm7
618
619	movq	%rbp, %rsp
620	popq	%rbp
621
622	addq	$8, %rsp	/* pop 1st plt-pushed args */
623				/* the second arguement is used */
624				/* for the 'return' address to our */
625				/* final destination */
626
627	ret			/* invoke resolved function */
628	.size 	elf_rtbndr, .-elf_rtbndr
629#endif
630