2000-05-10 Jakub Jelinek <jakub@redhat.com>
[kopensolaris-gnu/glibc.git] / sysdeps / sparc / sparc64 / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  Sparc64 version.
2    Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If
17    not, write to the Free Software Foundation, Inc.,
18    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #define ELF_MACHINE_NAME "sparc64"
21
22 #include <string.h>
23 #include <sys/param.h>
24 #include <ldsodefs.h>
25 #include <sysdep.h>
26
27 #define ELF64_R_TYPE_ID(info)   ((info) & 0xff)
28 #define ELF64_R_TYPE_DATA(info) ((info) >> 8)
29
30 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
31 static inline int
32 elf_machine_matches_host (Elf64_Half e_machine)
33 {
34   return e_machine == EM_SPARCV9;
35 }
36
37 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
38    first element of the GOT.  This must be inlined in a function which
39    uses global data.  */
40 static inline Elf64_Addr
41 elf_machine_dynamic (void)
42 {
43   register Elf64_Addr *elf_pic_register __asm__("%l7");
44
45   return *elf_pic_register;
46 }
47
48 /* Return the run-time load address of the shared object.  */
49 static inline Elf64_Addr
50 elf_machine_load_address (void)
51 {
52   register Elf64_Addr elf_pic_register __asm__("%l7");
53   Elf64_Addr pc, la;
54
55   /* Utilize the fact that a local .got entry will be partially
56      initialized at startup awaiting its RELATIVE fixup.  */
57
58   __asm("sethi %%hi(.Load_address), %1\n"
59         ".Load_address:\n\t"
60         "rd %%pc, %0\n\t"
61         "or %1, %%lo(.Load_address), %1\n\t"
62         : "=r"(pc), "=r"(la));
63
64   return pc - *(Elf64_Addr *)(elf_pic_register + la);
65 }
66
67 /* We have 4 cases to handle.  And we code different code sequences
68    for each one.  I love V9 code models...  */
69 static inline Elf64_Addr
70 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
71                        const Elf64_Rela *reloc,
72                        Elf64_Addr *reloc_addr, Elf64_Addr value)
73 {
74   unsigned int *insns = (unsigned int *) reloc_addr;
75   Elf64_Addr plt_vaddr = (Elf64_Addr) reloc_addr;
76
77   /* Now move plt_vaddr up to the call instruction.  */
78   plt_vaddr += (2 * 4);
79
80   /* PLT entries .PLT32768 and above look always the same.  */
81   if (__builtin_expect (reloc->r_addend, 0) != 0)
82     {
83       *reloc_addr = value - map->l_addr;
84     }
85   /* 32-bit Sparc style, the target is in the lower 32-bits of
86      address space.  */
87   else if ((value >> 32) == 0)
88     {
89       /* sethi  %hi(target), %g1
90          jmpl   %g1 + %lo(target), %g0  */
91
92       insns[2] = 0x81c06000 | (value & 0x3ff);
93       __asm __volatile ("flush %0 + 8" : : "r" (insns));
94
95       insns[1] = 0x03000000 | ((unsigned int)(value >> 10));
96       __asm __volatile ("flush %0 + 4" : : "r" (insns));
97     }
98   /* We can also get somewhat simple sequences if the distance between
99      the target and the PLT entry is within +/- 2GB.  */
100   else if ((plt_vaddr > value
101             && ((plt_vaddr - value) >> 32) == 0)
102            || (value > plt_vaddr
103                && ((value - plt_vaddr) >> 32) == 0))
104     {
105       unsigned int displacement;
106
107       if (plt_vaddr > value)
108         displacement = (0 - (plt_vaddr - value));
109       else
110         displacement = value - plt_vaddr;
111
112       /* mov    %o7, %g1
113          call   displacement
114           mov   %g1, %o7  */
115
116       insns[3] = 0x9e100001;
117       __asm __volatile ("flush %0 + 12" : : "r" (insns));
118
119       insns[2] = 0x40000000 | (displacement >> 2);
120       __asm __volatile ("flush %0 + 8" : : "r" (insns));
121
122       insns[1] = 0x8210000f;
123       __asm __volatile ("flush %0 + 4" : : "r" (insns));
124     }
125   /* Worst case, ho hum...  */
126   else
127     {
128       unsigned int high32 = (value >> 32);
129       unsigned int low32 = (unsigned int) value;
130
131       /* ??? Some tricks can be stolen from the sparc64 egcs backend
132              constant formation code I wrote.  -DaveM  */
133
134       /* sethi  %hh(value), %g1
135          sethi  %lm(value), %g5
136          or     %g1, %hm(value), %g1
137          or     %g5, %lo(value), %g5
138          sllx   %g1, 32, %g1
139          jmpl   %g1 + %g5, %g0
140           nop  */
141
142       insns[6] = 0x81c04005;
143       __asm __volatile ("flush %0 + 24" : : "r" (insns));
144
145       insns[5] = 0x83287020;
146       __asm __volatile ("flush %0 + 20" : : "r" (insns));
147
148       insns[4] = 0x8a116000 | (low32 & 0x3ff);
149       __asm __volatile ("flush %0 + 16" : : "r" (insns));
150
151       insns[3] = 0x82106000 | (high32 & 0x3ff);
152       __asm __volatile ("flush %0 + 12" : : "r" (insns));
153
154       insns[2] = 0x0b000000 | (low32 >> 10);
155       __asm __volatile ("flush %0 + 8" : : "r" (insns));
156
157       insns[1] = 0x03000000 | (high32 >> 10);
158       __asm __volatile ("flush %0 + 4" : : "r" (insns));
159     }
160
161   return value;
162 }
163
164 /* Return the final value of a plt relocation.  */
165 static inline Elf64_Addr
166 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
167                        Elf64_Addr value)
168 {
169   return value + reloc->r_addend;
170 }
171
172 #ifdef RESOLVE
173
174 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
175    MAP is the object containing the reloc.  */
176
177 static inline void
178 elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
179                   const Elf64_Sym *sym, const struct r_found_version *version,
180                   Elf64_Addr *const reloc_addr)
181 {
182 #ifndef RTLD_BOOTSTRAP
183   /* This is defined in rtld.c, but nowhere in the static libc.a; make the
184      reference weak so static programs can still link.  This declaration
185      cannot be done when compiling rtld.c (i.e.  #ifdef RTLD_BOOTSTRAP)
186      because rtld.c contains the common defn for _dl_rtld_map, which is
187      incompatible with a weak decl in the same file.  */
188   weak_extern (_dl_rtld_map);
189 #endif
190
191   if (ELF64_R_TYPE_ID (reloc->r_info) == R_SPARC_RELATIVE)
192     {
193 #ifndef RTLD_BOOTSTRAP
194       if (map != &_dl_rtld_map) /* Already done in rtld itself. */
195 #endif
196         *reloc_addr = map->l_addr + reloc->r_addend;
197     }
198   else if (ELF64_R_TYPE_ID (reloc->r_info) != R_SPARC_NONE) /* Who is Wilbur? */
199     {
200       const Elf64_Sym *const refsym = sym;
201       Elf64_Addr value;
202       if (sym->st_shndx != SHN_UNDEF &&
203           ELF64_ST_BIND (sym->st_info) == STB_LOCAL)
204         value = map->l_addr;
205       else
206         {
207           value = RESOLVE (&sym, version, ELF64_R_TYPE_ID (reloc->r_info));
208           if (sym)
209             value += sym->st_value;
210         }
211       value += reloc->r_addend; /* Assume copy relocs have zero addend.  */
212
213       switch (ELF64_R_TYPE_ID (reloc->r_info))
214         {
215         case R_SPARC_COPY:
216           if (sym == NULL)
217             /* This can happen in trace mode if an object could not be
218                found.  */
219             break;
220           if (sym->st_size > refsym->st_size
221               || (_dl_verbose && sym->st_size < refsym->st_size))
222             {
223               extern char **_dl_argv;
224               const char *strtab;
225
226               strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
227               _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
228                                 ": Symbol `", strtab + refsym->st_name,
229                                 "' has different size in shared object, "
230                                 "consider re-linking\n", NULL);
231             }
232           memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
233                                                    refsym->st_size));
234           break;
235
236         case R_SPARC_64:
237         case R_SPARC_GLOB_DAT:
238           *reloc_addr = value;
239           break;
240         case R_SPARC_8:
241           *(char *) reloc_addr = value;
242           break;
243         case R_SPARC_16:
244           *(short *) reloc_addr = value;
245           break;
246         case R_SPARC_32:
247           *(unsigned int *) reloc_addr = value;
248           break;
249         case R_SPARC_DISP8:
250           *(char *) reloc_addr = (value - (Elf64_Addr) reloc_addr);
251           break;
252         case R_SPARC_DISP16:
253           *(short *) reloc_addr = (value - (Elf64_Addr) reloc_addr);
254           break;
255         case R_SPARC_DISP32:
256           *(unsigned int *) reloc_addr = (value - (Elf64_Addr) reloc_addr);
257           break;
258         case R_SPARC_WDISP30:
259           *(unsigned int *) reloc_addr =
260             ((*(unsigned int *)reloc_addr & 0xc0000000) |
261              ((value - (Elf64_Addr) reloc_addr) >> 2));
262           break;
263
264         /* MEDLOW code model relocs */
265         case R_SPARC_LO10:
266           *(unsigned int *) reloc_addr =
267             ((*(unsigned int *)reloc_addr & ~0x3ff) |
268              (value & 0x3ff));
269           break;
270         case R_SPARC_HI22:
271           *(unsigned int *) reloc_addr =
272             ((*(unsigned int *)reloc_addr & 0xffc00000) |
273              (value >> 10));
274           break;
275         case R_SPARC_OLO10:
276           *(unsigned int *) reloc_addr =
277             ((*(unsigned int *)reloc_addr & ~0x1fff) |
278              (((value & 0x3ff) + ELF64_R_TYPE_DATA (reloc->r_info)) & 0x1fff));
279           break;
280
281         /* MEDMID code model relocs */
282         case R_SPARC_H44:
283           *(unsigned int *) reloc_addr =
284             ((*(unsigned int *)reloc_addr & 0xffc00000) |
285              (value >> 22));
286           break;
287         case R_SPARC_M44:
288           *(unsigned int *) reloc_addr =
289             ((*(unsigned int *)reloc_addr & ~0x3ff) |
290              ((value >> 12) & 0x3ff));
291           break;
292         case R_SPARC_L44:
293           *(unsigned int *) reloc_addr =
294             ((*(unsigned int *)reloc_addr & ~0xfff) |
295              (value & 0xfff));
296           break;
297
298         /* MEDANY code model relocs */
299         case R_SPARC_HH22:
300           *(unsigned int *) reloc_addr =
301             ((*(unsigned int *)reloc_addr & 0xffc00000) |
302              (value >> 42));
303           break;
304         case R_SPARC_HM10:
305           *(unsigned int *) reloc_addr =
306             ((*(unsigned int *)reloc_addr & ~0x3ff) |
307              ((value >> 32) & 0x3ff));
308           break;
309         case R_SPARC_LM22:
310           *(unsigned int *) reloc_addr =
311             ((*(unsigned int *)reloc_addr & 0xffc00000) |
312              ((value >> 10) & 0x003fffff));
313           break;
314
315         case R_SPARC_JMP_SLOT:
316           elf_machine_fixup_plt(map, NULL, reloc, reloc_addr, value);
317           break;
318
319         case R_SPARC_UA64:
320           if (! ((long) reloc_addr & 3))
321             {
322               /* Common in .eh_frame */
323               ((unsigned int *) reloc_addr) [0] = value >> 32;
324               ((unsigned int *) reloc_addr) [1] = value;
325               break;
326             }
327           ((unsigned char *) reloc_addr) [0] = value >> 56;
328           ((unsigned char *) reloc_addr) [1] = value >> 48;
329           ((unsigned char *) reloc_addr) [2] = value >> 40;
330           ((unsigned char *) reloc_addr) [3] = value >> 32;
331           ((unsigned char *) reloc_addr) [4] = value >> 24;
332           ((unsigned char *) reloc_addr) [5] = value >> 16;
333           ((unsigned char *) reloc_addr) [6] = value >> 8;
334           ((unsigned char *) reloc_addr) [7] = value;
335           break;
336
337         default:
338           _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 0);
339           break;
340         }
341     }
342 }
343
344 static inline void
345 elf_machine_lazy_rel (struct link_map *map,
346                       Elf64_Addr l_addr, const Elf64_Rela *reloc)
347 {
348   switch (ELF64_R_TYPE (reloc->r_info))
349     {
350     case R_SPARC_NONE:
351       break;
352     case R_SPARC_JMP_SLOT:
353       break;
354     default:
355       _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 1);
356       break;
357     }
358 }
359
360 #endif  /* RESOLVE */
361
362 /* Nonzero iff TYPE should not be allowed to resolve to one of
363    the main executable's symbols, as for a COPY reloc.  */
364 #define elf_machine_lookup_noexec_p(type) ((type) == R_SPARC_COPY)
365
366 /* Nonzero iff TYPE describes relocation of a PLT entry, so
367    PLT entries should not be allowed to define the value.  */
368 #define elf_machine_lookup_noplt_p(type) ((type) == R_SPARC_JMP_SLOT)
369
370 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
371 #define ELF_MACHINE_JMP_SLOT    R_SPARC_JMP_SLOT
372
373 /* The SPARC never uses Elf64_Rel relocations.  */
374 #define ELF_MACHINE_NO_REL 1
375
376 /* The SPARC overlaps DT_RELA and DT_PLTREL.  */
377 #define ELF_MACHINE_PLTREL_OVERLAP 1
378
379 /* Set up the loaded object described by L so its unrelocated PLT
380    entries will jump to the on-demand fixup code in dl-runtime.c.  */
381
382 static inline int
383 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
384 {
385   if (l->l_info[DT_JMPREL] && lazy)
386     {
387       extern void _dl_runtime_resolve_0 (void);
388       extern void _dl_runtime_resolve_1 (void);
389       extern void _dl_runtime_profile_0 (void);
390       extern void _dl_runtime_profile_1 (void);
391       Elf64_Addr res0_addr, res1_addr;
392       unsigned int *plt = (void *) D_PTR (l, l_info[DT_PLTGOT]);
393
394       if (! profile)
395         {
396           res0_addr = (Elf64_Addr) &_dl_runtime_resolve_0;
397           res1_addr = (Elf64_Addr) &_dl_runtime_resolve_1;
398         }
399       else
400         {
401           res0_addr = (Elf64_Addr) &_dl_runtime_profile_0;
402           res1_addr = (Elf64_Addr) &_dl_runtime_profile_1;
403           if (_dl_name_match_p (_dl_profile, l))
404             _dl_profile_map = l;
405         }
406
407       /* PLT0 looks like:
408
409          save   %sp, -192, %sp
410          sethi  %hh(_dl_runtime_{resolve,profile}_0), %l0
411          sethi  %lm(_dl_runtime_{resolve,profile}_0), %l1
412          or     %l0, %hm(_dl_runtime_{resolve,profile}_0), %l0
413          or     %l1, %lo(_dl_runtime_{resolve,profile}_0), %l1
414          sllx   %l0, 32, %l0
415          jmpl   %l0 + %l1, %l6
416           sethi %hi(0xffc00), %l2
417        */
418
419       plt[0] = 0x9de3bf40;
420       plt[1] = 0x21000000 | (res0_addr >> (64 - 22));
421       plt[2] = 0x23000000 | ((res0_addr >> 10) & 0x003fffff);
422       plt[3] = 0xa0142000 | ((res0_addr >> 32) & 0x3ff);
423       plt[4] = 0xa2146000 | (res0_addr & 0x3ff);
424       plt[5] = 0xa12c3020;
425       plt[6] = 0xadc40011;
426       plt[7] = 0x250003ff;
427
428       /* PLT1 looks like:
429
430          save   %sp, -192, %sp
431          sethi  %hh(_dl_runtime_{resolve,profile}_1), %l0
432          sethi  %lm(_dl_runtime_{resolve,profile}_1), %l1
433          or     %l0, %hm(_dl_runtime_{resolve,profile}_1), %l0
434          or     %l1, %lo(_dl_runtime_{resolve,profile}_1), %l1
435          sllx   %l0, 32, %l0
436          jmpl   %l0 + %l1, %l6
437           srlx  %g1, 12, %o1
438        */
439
440       plt[8 + 0] = 0x9de3bf40;
441       plt[8 + 1] = 0x21000000 | (res1_addr >> (64 - 22));
442       plt[8 + 2] = 0x23000000 | ((res1_addr >> 10) & 0x003fffff);
443       plt[8 + 3] = 0xa0142000 | ((res1_addr >> 32) & 0x3ff);
444       plt[8 + 4] = 0xa2146000 | (res1_addr & 0x3ff);
445       plt[8 + 5] = 0xa12c3020;
446       plt[8 + 6] = 0xadc40011;
447       plt[8 + 7] = 0x9330700c;
448
449       /* Now put the magic cookie at the beginning of .PLT3
450          Entry .PLT4 is unused by this implementation.  */
451       *((struct link_map **)(&plt[16 + 0])) = l;
452     }
453
454   return lazy;
455 }
456
457 /* This code is used in dl-runtime.c to call the `fixup' function
458    and then redirect to the address it returns.  */
459 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name)     \
460   asm ("\
461         .text
462         .globl  " #tramp_name "_0
463         .type   " #tramp_name "_0, @function
464         .align  32
465 " #tramp_name "_0:
466         ! sethi   %hi(1047552), %l2 - Done in .PLT0
467         ldx     [%l6 + 32 + 8], %o0
468         sub     %g1, %l6, %l0
469         xor     %l2, -1016, %l2
470         sethi   %hi(5120), %l3
471         add     %l0, %l2, %l0
472         sethi   %hi(32768), %l4
473         udivx   %l0, %l3, %l3
474         sllx    %l3, 2, %l1
475         add     %l1, %l3, %l1
476         sllx    %l1, 10, %l2
477         sllx    %l1, 5, %l1
478         sub     %l0, %l2, %l0
479         udivx   %l0, 24, %l0
480         add     %l0, %l4, %l0
481         add     %l1, %l0, %l1
482         add     %l1, %l1, %l0
483         add     %l0, %l1, %l0
484         mov     %i7, %o2
485         call    " #fixup_name "
486          sllx    %l0, 3, %o1
487         jmp     %o0
488          restore
489         .size   " #tramp_name "_0, . - " #tramp_name "_0
490
491         .globl  " #tramp_name "_1
492         .type   " #tramp_name "_1, @function
493         .align  32
494 " #tramp_name "_1:
495         ! srlx  %g1, 12, %o1 - Done in .PLT1
496         ldx     [%l6 + 8], %o0
497         add     %o1, %o1, %o3
498         mov     %i7, %o2
499         call    " #fixup_name "
500          add    %o1, %o3, %o1
501         jmp     %o0
502          restore
503         .size   " #tramp_name "_1, . - " #tramp_name "_1
504         .previous");
505
506 #ifndef PROF
507 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                  \
508   TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);     \
509   TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
510 #else
511 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                  \
512   TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);     \
513   TRAMPOLINE_TEMPLATE (_dl_runtime_profile, fixup);
514 #endif
515
516 /* The PLT uses Elf64_Rela relocs.  */
517 #define elf_machine_relplt elf_machine_rela
518
519 /* Initial entry point code for the dynamic linker.
520    The C function `_dl_start' is the real entry point;
521    its return value is the user program's entry point.  */
522
523 #define __S1(x) #x
524 #define __S(x)  __S1(x)
525
526 #define RTLD_START __asm__ ( "\
527         .text
528         .global _start
529         .type   _start, @function
530         .align  32
531 _start:
532    /* Make room for functions to drop their arguments on the stack.  */
533         sub     %sp, 6*8, %sp
534    /* Pass pointer to argument block to _dl_start.  */
535         call    _dl_start
536          add     %sp," __S(STACK_BIAS) "+22*8,%o0
537         /* FALLTHRU */
538         .size _start, .-_start
539
540         .global _dl_start_user
541         .type   _dl_start_user, @function
542 _dl_start_user:
543    /* Load the GOT register.  */
544 1:      call    11f
545         sethi   %hi(_GLOBAL_OFFSET_TABLE_-(1b-.)),%l7
546 11:     or      %l7,%lo(_GLOBAL_OFFSET_TABLE_-(1b-.)),%l7
547         add     %l7,%o7,%l7
548    /* Save the user entry point address in %l0.  */
549         mov     %o0,%l0
550   /* Store the highest stack address.  */
551         sethi   %hi(__libc_stack_end), %g5
552         or      %g5, %lo(__libc_stack_end), %g5
553         ldx     [%l7 + %g5], %l1
554         add     %sp, 6*8, %l2
555         stx     %l2, [%l1]
556    /* See if we were run as a command with the executable file name as an
557       extra leading argument.  If so, we must shift things around since we
558       must keep the stack doubleword aligned.  */
559         sethi   %hi(_dl_skip_args), %g5
560         or      %g5, %lo(_dl_skip_args), %g5
561         ldx     [%l7+%g5], %i0
562         ld      [%i0], %i0
563         brz,pt  %i0, 2f
564          ldx    [%sp+" __S(STACK_BIAS) "+22*8], %i5
565         /* Find out how far to shift.  */
566         sub     %i5, %i0, %i5
567         sllx    %i0, 3, %i2
568         stx     %i5, [%sp+" __S(STACK_BIAS) "+22*8]
569         add     %sp, " __S(STACK_BIAS) "+23*8, %i1
570         add     %i1, %i2, %i2
571         /* Copy down argv.  */
572 12:     ldx     [%i2], %i3
573         add     %i2, 8, %i2
574         stx     %i3, [%i1]
575         brnz,pt %i3, 12b
576          add    %i1, 8, %i1
577         /* Copy down envp.  */
578 13:     ldx     [%i2], %i3
579         add     %i2, 8, %i2
580         stx     %i3, [%i1]
581         brnz,pt %i3, 13b
582          add    %i1, 8, %i1
583         /* Copy down auxiliary table.  */
584 14:     ldx     [%i2], %i3
585         ldx     [%i2+8], %i4
586         add     %i2, 16, %i2
587         stx     %i3, [%i1]
588         stx     %i4, [%i1+8]
589         brnz,pt %i3, 13b
590          add    %i1, 16, %i1
591   /* %o0 = _dl_loaded, %o1 = argc, %o2 = argv, %o3 = envp.  */
592 2:      sethi   %hi(_dl_loaded), %o0
593         add     %sp, " __S(STACK_BIAS) "+23*8, %o2
594         orcc    %o0, %lo(_dl_loaded), %o0
595         sllx    %i5, 3, %o3
596         ldx     [%l7+%o0], %o0
597         add     %o3, 8, %o3
598         mov     %i5, %o1
599         add     %o2, %o3, %o3
600         call    _dl_init
601          ldx    [%o0], %o0
602    /* Pass our finalizer function to the user in %g1.  */
603         sethi   %hi(_dl_fini), %g1
604         or      %g1, %lo(_dl_fini), %g1
605         ldx     [%l7+%g1], %g1
606   /* Jump to the user's entry point and deallocate the extra stack we got.  */
607         jmp     %l0
608          add    %sp, 6*8, %sp
609         .size   _dl_start_user, . - _dl_start_user
610         .previous");