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.
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.
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]
23 * Copyright (c) 1988 AT&T
26 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
29 #pragma ident "%Z%%M% %I% %E% SMI"
36 #include <sys/types.h>
39 #include <sys/stack.h>
40 #include <sys/asm_linkage.h>
47 * We got here because the initial call to a function resolved to a procedure
48 * linkage table entry. That entry did a branch to the first PLT entry, which
49 * in turn did a call to elf_rtbndr (refer elf_plt_init()).
51 * the code sequence that got us here was:
53 * PLT entry for foo():
54 * sethi (.-PLT0), %g1 ! not changed by rtld
55 * ba,a .PLT0 ! patched atomically 2nd
58 * Therefore on entry, %i7 has the address of the call, which will be added
59 * to the offset to the plt entry in %g1 to calculate the plt entry address
60 * we must also subtract 4 because the address of PLT0 points to the
61 * save instruction before the call.
63 * the plt entry is rewritten:
65 * PLT entry for foo():
67 * sethi %hi(entry_pt), %g1
68 * jmpl %g1 + %lo(entry_pt), %g0
73 extern unsigned long elf_bndr
(Rt_map
*, unsigned long
, caddr_t
);
76 elf_rtbndr
(Rt_map
*lmp
, unsigned long pltoff
, caddr_t from
)
78 (void
) elf_bndr
(lmp
, pltoff
, from
);
83 .weak _elf_rtbndr ! keep dbx happy as it likes to
84 _elf_rtbndr
= elf_rtbndr
! rummage around for our symbols
87 .type elf_rtbndr, #function
91 mov
%i7
, %o0
! Save callers address
(profiling
)
92 save
%sp
, -SA
(MINFRAME
), %sp
! Make
a frame
93 srl
%g1
, 10, %o1
! shift offset set by sethi
94 ! %o1 has offset from jump slot
95 ! to PLT0 which will
be used to
96 ! calculate plt relocation entry
98 ld [%i7
+ 8], %o0
! %o0 has ptr to
lm
99 call elf_bndr
! returns function address in
%o0
100 mov
%i0
, %o2
! Callers address is arg
3
101 mov
%o0
, %g1
! save address of routine binded
102 restore
! how many restores needed ?
2
105 .size elf_rtbndr, . - elf_rtbndr
112 iflush_range
(caddr_t addr
, size_t len
)
117 base
= (uintptr_t
)addr
& ~
7;
/* round down to 8 byte alignment */
118 len
= (len
+ 7) & ~
7;
/* round up to multiple of 8 bytes */
119 for
(len
-= 8;
(long
)len
>= 0; len
-= 8)
120 /* iflush(base + len) */;
132 SET_SIZE
(iflush_range
)
136 * Initialize the first plt entry so that function calls go to elf_rtbndr
138 * The first plt entry (PLT0) is:
149 elf_plt_init
(void
*plt
, caddr_t lmp
)
151 *((uint_t
*)plt
+ 0) = (unsigned long
) M_SAVESP64;
152 *((uint_t
*)plt
+ 4) = M_CALL |
(((unsigned long
)elf_rtbndr
-
153 ((unsigned long
)plt
)) >> 2);
154 *((uint_t
*)plt
+ 8) = M_NOP;
155 *((uint_t
*)plt
+ 12) = (unsigned long
) lmp;
160 .type elf_plt_init, #function
164 save
%sp
, -SA
(MINFRAME
), %sp
! Make
a frame
167 sethi
%hi
((_GLOBAL_OFFSET_TABLE_
- (1b - .))), %l7
169 sethi
%hi
(M_SAVESP64
), %o0
! Get save instruction
170 or %o0
, %lo
(M_SAVESP64
), %o0
171 or %l7
, %lo
((_GLOBAL_OFFSET_TABLE_
- (1b - .))), %l7
172 st %o0
, [%i0
] ! Store in plt
[0]
175 ld [%l7
+ elf_rtbndr
], %l7
176 inc
4, %i0
! Bump plt to point to plt
[1]
177 sub %l7
, %i0
, %o0
! Determine
-pc so as to produce
179 srl
%o0
, 2, %o0
! Express offset as number of words
180 sethi
%hi
(M_CALL
), %o4
! Get sethi instruction
181 or %o4
, %o0
, %o4
! Add elf_rtbndr address
182 st %o4
, [%i0
] ! Store instruction in plt
184 sethi
%hi
(M_NOP
), %o0
! Generate
nop instruction
185 st %o0
, [%i0
+ 4] ! Store instruction in plt
[2]
187 st %i1
, [%i0
+ 8] ! Store instruction in plt
[3]
191 .size elf_plt_init, . - elf_plt_init
202 .global elf_plt_trace
203 .type elf_plt_trace, #function
207 * The dyn_plt that called us has already created a stack-frame for
208 * us and placed the following entries in it:
210 * [%fp - 0x4] * dyndata
211 * [%fp - 0x8] * prev stack size
213 * dyndata currently contains:
216 * 0x0 uintptr_t *reflmp
217 * 0x4 uintptr_t *deflmp
219 * 0xc ulong_t sb_flags
220 * 0x10 Sym symdef.st_name
221 * 0x14 symdef.st_value
222 * 0x18 symdef.st_size
223 * 0x1c symdef.st_info
224 * 0x1d symdef.st_other
225 * 0x1e symdef.st_shndx
227 #define REFLMP_OFF 0x0
228 #define DEFLMP_OFF 0x4
229 #define SYMNDX_OFF 0x8
230 #define SBFLAGS_OFF 0xc
231 #define SYMDEF_OFF 0x10
232 #define SYMDEF_VALUE_OFF 0x14
236 sethi
%hi
(_GLOBAL_OFFSET_TABLE_+
(.-1b)), %l7
237 2: or %l7
, %lo
(_GLOBAL_OFFSET_TABLE_+
(.-1b)), %l7
240 ld [%l7+audit_flags
], %l3
241 ld [%l3
], %l3
! %l3
= audit_flags
242 andcc
%l3
, AF_PLTENTER
, %g0
244 ld [%fp
+ -0x4], %l1
! l1
= * dyndata
245 ld [%l1
+ SBFLAGS_OFF
], %l2
! l2
= sb_flags
246 andcc
%l2
, LA_SYMB_NOPLTENTER
, %g0
248 ld [%l1
+ SYMDEF_VALUE_OFF
], %l0
! l0
=
249 ! sym.st_value
(calling address
)
254 * save all registers into La_sparcv8_regs
257 sub %sp
, 0x20, %sp
! create space for La_sparcv8_regs
258 ! storage on the stack.
265 st %i3
, [%o4
+ 0xc] ! because
a regwindow shift has
266 st %i4
, [%o4
+ 0x10] ! already occured our current
%i
*
267 st %i5
, [%o4
+ 0x14] ! register
's are the equivalent of
268 st %i6, [%o4 + 0x18] ! the %o* registers that the final
269 st %i7, [%o4 + 0x1c] ! procedure shall see.
271 ld [%fp + -0x4], %l1 ! %l1 == * dyndata
272 ld [%l1 + REFLMP_OFF], %o0 ! %o0 = reflmp
273 ld [%l1 + DEFLMP_OFF], %o1 ! %o1 = deflmp
274 add %l1, SYMDEF_OFF, %o2 ! %o2 = symp
275 ld [%l1 + SYMNDX_OFF], %o3 ! %o3 = symndx
277 add %l1, SBFLAGS_OFF, %o5 ! %o3 = * sb_flags
279 mov %o0, %l0 ! %l0 == calling address
281 add %sp, 0x20, %sp ! cleanup La_sparcv8_regs off
286 * If *no* la_pltexit() routines exist we do not need to keep the
287 * stack frame before we call the actual routine. Instead we jump to
288 * it and remove our self from the stack at the same time.
290 ld [%l7+audit_flags], %l3
291 ld [%l3], %l3 ! %l3 = audit_flags
292 andcc %l3, AF_PLTEXIT, %g0
294 ld [%fp + -0x4], %l1 ! %l1 = * dyndata
295 ld [%l1 + SBFLAGS_OFF], %l2 ! %l2 = sb_flags
296 andcc %l2, LA_SYMB_NOPLTEXIT, %g0
308 * In order to call la_pltexit() we must duplicate the
309 * arguments from the 'callers
' stack on our stack frame.
311 * First we check the size of the callers stack and grow
312 * our stack to hold any of the arguments. That need
313 * duplicating (these are arguments 6->N), because the
314 * first 6 (0->5) are passed via register windows on sparc.
318 * The first calculation is to determine how large the
319 * argument passing area might be. Since there is no
320 * way to distinquish between 'argument passing
' and
321 * 'local storage
' from the previous stack this amount must
324 ld [%fp + -0x8], %l1 ! %l1 = callers stack size
325 sub %l1, 0x58, %l1 ! %l1 = argument space on caller's
328 * Next we compare the prev. stack size against the audit_argcnt.
329 * We copy at most 'audit_argcnt' arguments.
331 * NOTE: on sparc we always copy at least six args since these
332 * are in reg-windows and not on the stack.
334 * NOTE: Also note that we multiply (shift really) the arg count
335 * by 4 which is the 'word size' to calculate the amount
336 * of stack space needed.
338 ld [%l7
+ audit_argcnt
], %l2
339 ld [%l2
], %l2
! %l2
= audit_arg_count
350 * When duplicating the stack we skip the first '0x5c' bytes.
351 * This is the space on the stack reserved for preserving
352 * the register windows and such and do not need to be duplicated
353 * on this new stack frame. We start duplicating at the
354 * portion of the stack reserved for argument's above 6.
356 sub %sp
, %l1
, %sp
! grow our stack by amount required.
357 sra %l1
, 0x2, %l1
! %l1
= %l1
/ 4 (words to copy
)
358 mov
0x5c, %l2
! %l2
= index into stack
& frame
364 ld [%fp
+ %l2
], %l3
! duplicate args from previous
365 st %l3
, [%sp
+ %l2
] ! stack onto current stack
370 mov
%i0
, %o0
! copy ins to outs
375 call
%l0
! call routine
377 mov
%o1
, %l2
! l2
= second
1/2 of return value
378 ! for those those
64 bit operations
379 ! link div64
- yuck.
..
383 ld [%l1
+ REFLMP_OFF
], %o1
! %o1
= reflmp
384 ld [%l1
+ DEFLMP_OFF
], %o2
! %o2
= deflmp
385 add %l1
, SYMDEF_OFF
, %o3
! %o3
= symp
387 ld [%l1
+ SYMNDX_OFF
], %o4
! %o4
= symndx
389 mov
%o0
, %i0
! pass on return code
393 .size elf_plt_trace, . - elf_plt_trace
398 * After the first call to a plt, elf_bndr() will have determined the true
399 * address of the function being bound. The plt is now rewritten so that
400 * any subsequent calls go directly to the bound function. If the library
401 * to which the function belongs is being profiled refer to _plt_cg_write.
403 * the new plt entry is:
405 * sethi (.-PLT0), %g1 ! constant
406 * sethi %hi(function address), %g1 ! patched second
407 * jmpl %g1 + %lo(function address, %g0 ! patched first
413 plt_full_range
(uintptr_t pc
, uintptr_t symval
)
415 uint_t
* plttab
= (uint_t
*)pc;
416 plttab
[2] = (M_JMPL |
((unsigned long
)symval
& S_MASK
(10)));
417 plttab
[1] = (M_SETHIG1 |
((unsigned long
)symval
>> (32 - 22)));
421 ENTRY
(plt_full_range
)
423 sethi
%hi
(M_JMPL
), %o3
! Get jmpl instruction
424 and %o1
, 0x3ff, %o2
! Lower part of function address
425 or %o3
, %o2
, %o3
! is
or'ed into instruction
426 st %o3, [%o0 + 8] ! Store instruction in plt[2]
430 srl %o1, 10, %o1 ! Get high part of function address
431 sethi %hi(M_SETHIG1), %o3 ! Get sethi instruction
432 or %o3, %o1, %o3 ! Add sethi and function address
433 st %o3, [%o0 + 4] ! Store instruction in plt[1]
437 SET_SIZE(plt_full_range)
439 #endif /* defined(lint) */