Update.
[kopensolaris-gnu/glibc.git] / sysdeps / mips / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  MIPS version.
2    Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Kazumoto Kojima <kkojima@info.kanagawa-u.ac.jp>.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #ifndef dl_machine_h
22 #define dl_machine_h
23
24 #define ELF_MACHINE_NAME "MIPS"
25
26 #define ELF_MACHINE_NO_PLT
27
28 #include <assert.h>
29 #include <entry.h>
30
31 #ifndef ENTRY_POINT
32 #error ENTRY_POINT needs to be defined for MIPS.
33 #endif
34
35 #ifndef _RTLD_PROLOGUE
36 #ifdef __STDC__
37 #define _RTLD_PROLOGUE(entry) "\n\t.globl " #entry \
38                               "\n\t.ent " #entry \
39                               "\n\t" #entry ":\n\t"
40 #else
41 #define _RTLD_PROLOGUE(entry) "\n\t.globl entry\n\t.ent entry\n\t entry:\n\t"
42 #endif
43 #endif
44
45 #ifndef _RTLD_EPILOGUE
46 #ifdef __STDC__
47 #define _RTLD_EPILOGUE(entry) "\t.end " #entry "\n"
48 #else
49 #define _RTLD_EPILOGUE(entry) "\t.end entry\n"
50 #endif
51 #endif
52
53 /* I have no idea what I am doing. */
54 #define ELF_MACHINE_RELOC_NOPLT                 -1
55 #define elf_machine_lookup_noplt_p(type)        (1)
56 #define elf_machine_lookup_noexec_p(type)       (0)
57
58 /* Translate a processor specific dynamic tag to the index
59    in l_info array.  */
60 #define DT_MIPS(x) (DT_MIPS_##x - DT_LOPROC + DT_NUM)
61
62 #if 0
63 /* We may need 64k alignment. */
64 #define ELF_MACHINE_ALIGN_MASK 0xffff
65 #endif
66
67 /*
68  * MIPS libraries are usually linked to a non-zero base address.  We
69  * subtrace the base address from the address where we map the object
70  * to.  This results in more efficient address space usage.
71  */
72 #if 0
73 #define MAP_BASE_ADDR(l) ((l)->l_info[DT_MIPS(BASE_ADDRESS)] ? \
74                           (l)->l_info[DT_MIPS(BASE_ADDRESS)]->d_un.d_ptr : 0)
75 #else
76 #define MAP_BASE_ADDR(l) 0x5ffe0000
77 #endif
78
79 /* If there is a DT_MIPS_RLD_MAP entry in the dynamic section, fill it in
80    with the run-time address of the r_debug structure  */
81 #define ELF_MACHINE_DEBUG_SETUP(l,r) \
82 do { if ((l)->l_info[DT_MIPS (RLD_MAP)]) \
83        *(ElfW(Addr) *)((l)->l_info[DT_MIPS (RLD_MAP)]->d_un.d_ptr) = \
84        (ElfW(Addr)) (r); \
85    } while (0)
86
87 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
88 static inline int __attribute__ ((unused))
89 elf_machine_matches_host (ElfW(Half) e_machine)
90 {
91   switch (e_machine)
92     {
93     case EM_MIPS:
94     case EM_MIPS_RS4_BE:
95       return 1;
96     default:
97       return 0;
98     }
99 }
100
101 static inline ElfW(Addr) *
102 elf_mips_got_from_gpreg (ElfW(Addr) gpreg)
103 {
104   /* FIXME: the offset of gp from GOT may be system-dependent. */
105   return (ElfW(Addr) *) (gpreg - 0x7ff0);
106 }
107
108 /* Return the run-time address of the _GLOBAL_OFFSET_TABLE_.
109    Must be inlined in a function which uses global data.  */
110 static inline ElfW(Addr) *
111 elf_machine_got (void)
112 {
113   ElfW(Addr) gp;
114
115   __asm__ __volatile__("move %0, $28\n\t" : "=r" (gp));
116   return elf_mips_got_from_gpreg (gp);
117 }
118
119
120 /* Return the run-time load address of the shared object.  */
121 static inline ElfW(Addr)
122 elf_machine_load_address (void)
123 {
124   ElfW(Addr) addr;
125   asm ("        .set noreorder\n"
126        "        la %0, here\n"
127        "        bltzal $0, here\n"
128        "        nop\n"
129        "here:   subu %0, $31, %0\n"
130        "        .set reorder\n"
131        :        "=r" (addr)
132        :        /* No inputs */
133        :        "$31");
134   return addr;
135 }
136
137 /* The MSB of got[1] of a gnu object is set to identify gnu objects. */
138 #define ELF_MIPS_GNU_GOT1_MASK 0x80000000
139
140 /* Relocate GOT. */
141 static inline void
142 elf_machine_got_rel (struct link_map *map, int lazy)
143 {
144   ElfW(Addr) *got;
145   ElfW(Sym) *sym;
146   int i, n;
147   const char *strtab = (const void *) map->l_info[DT_STRTAB]->d_un.d_ptr;
148
149 #define RESOLVE_GOTSYM(sym) \
150     ({ \
151       const ElfW(Sym) *ref = sym; \
152       ElfW(Addr) sym_loadaddr; \
153       sym_loadaddr = _dl_lookup_symbol (strtab + sym->st_name, &ref, \
154                                         map->l_scope, \
155                                         map->l_name, ELF_MACHINE_RELOC_NOPLT);\
156       (ref)? sym_loadaddr + ref->st_value: 0; \
157     })
158
159   got = (ElfW(Addr) *) map->l_info[DT_PLTGOT]->d_un.d_ptr;
160
161   /* got[0] is reserved. got[1] is also reserved for the dynamic object
162      generated by gnu ld. Skip these reserved entries from relocation.  */
163   i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2: 1;
164   n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
165   /* Add the run-time display to all local got entries. */
166   while (i < n)
167     got[i++] += map->l_addr;
168
169   /* Handle global got entries. */
170   got += n;
171   sym = (void *) map->l_info[DT_SYMTAB]->d_un.d_ptr;
172   sym += map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
173   i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val
174        - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val);
175
176   while (i--)
177     {
178       if (sym->st_shndx == SHN_UNDEF)
179         {
180           if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC)
181             {
182               if (sym->st_value && lazy)
183                 *got = sym->st_value + map->l_addr;
184               else
185                 *got = RESOLVE_GOTSYM (sym);
186             }
187           else /* if (*got == 0 || *got == QS) */
188             *got = RESOLVE_GOTSYM (sym);
189         }
190       else if (sym->st_shndx == SHN_COMMON)
191         *got = RESOLVE_GOTSYM (sym);
192       else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
193                && *got != sym->st_value
194                && lazy)
195         *got += map->l_addr;
196       else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
197         {
198           if (sym->st_other == 0)
199             *got += map->l_addr;
200         }
201       else
202         *got = RESOLVE_GOTSYM (sym);
203
204       got++;
205       sym++;
206     }
207
208 #undef RESOLVE_GOTSYM
209
210   return;
211 }
212
213 /* Set up the loaded object described by L so its stub function
214    will jump to the on-demand fixup code in dl-runtime.c.  */
215
216 static inline int
217 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
218 {
219   ElfW(Addr) *got;
220   extern void _dl_runtime_resolve (ElfW(Word));
221   extern int _dl_mips_gnu_objects;
222
223 #ifdef RTLD_BOOTSTRAP
224     {
225       return lazy;
226     }
227 #endif
228   if (lazy)
229     {
230       /* The GOT entries for functions have not yet been filled in.
231          Their initial contents will arrange when called to put an
232          offset into the .dynsym section in t8, the return address
233          in t7 and then jump to _GLOBAL_OFFSET_TABLE[0].  */
234       got = (ElfW(Addr) *) l->l_info[DT_PLTGOT]->d_un.d_ptr;
235
236       /* This function will get called to fix up the GOT entry indicated by
237          the register t8, and then jump to the resolved address.  */
238       got[0] = (ElfW(Addr)) &_dl_runtime_resolve;
239
240       /* Store l to _GLOBAL_OFFSET_TABLE[1] for gnu object. The MSB
241          of got[1] of a gnu object is set to identify gnu objects.
242          Where we can store l for non gnu objects? XXX  */
243       if ((got[1] & ELF_MIPS_GNU_GOT1_MASK) != 0)
244         got[1] = (ElfW(Addr)) ((unsigned) l | ELF_MIPS_GNU_GOT1_MASK);
245       else
246         _dl_mips_gnu_objects = 0;
247     }
248
249   /* Relocate global offset table.  */
250   elf_machine_got_rel (l, lazy);
251
252   return lazy;
253 }
254
255 /* Get link_map for this object.  */
256 static inline struct link_map *
257 elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
258 {
259   extern int _dl_mips_gnu_objects;
260
261   /* got[1] is reserved to keep its link map address for the shared
262      object generated by gnu linker. If all are such object, we can
263      find link map from current GPREG simply. If not so, get link map
264      for callers object containing STUB_PC.  */
265
266   if (_dl_mips_gnu_objects)
267     {
268       ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
269       ElfW(Word) g1;
270
271       g1 = ((ElfW(Word) *) got)[1];
272
273       if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
274         return (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
275     }
276
277     {
278       struct link_map *l = _dl_loaded;
279       struct link_map *ret = 0;
280       ElfW(Addr) candidate = 0;
281
282       while (l)
283         {
284           ElfW(Addr) base = 0;
285           const ElfW(Phdr) *p = l->l_phdr;
286           ElfW(Half) this, nent = l->l_phnum;
287
288           /* Get the base. */
289           for (this = 0; this < nent; this++)
290             if (p[this].p_type == PT_LOAD)
291               {
292                 base = p[this].p_vaddr + l->l_addr;
293                 break;
294               }
295           if (! base)
296             {
297               l = l->l_next;
298               continue;
299             }
300
301           /* Find closest link base addr. */
302           if ((base < stub_pc) && (candidate < base))
303             {
304               candidate = base;
305               ret = l;
306             }
307           l = l->l_next;
308         }
309       if (candidate && ret && (candidate < stub_pc))
310         return ret;
311       else if (!candidate)
312         return _dl_loaded;
313     }
314
315   _dl_signal_error (0, NULL, "cannot find runtime link map");
316   return NULL;
317 }
318
319 /* Mips has no PLT but define elf_machine_relplt to be elf_machine_rel. */
320 #define elf_machine_relplt elf_machine_rel
321
322 /* Define mips specific runtime resolver. The function __dl_runtime_resolve
323    is called from assembler function _dl_runtime_resolve which converts
324    special argument registers t7 ($15) and t8 ($24):
325      t7  address to return to the caller of the function
326      t8  index for this function symbol in .dynsym
327    to usual c arguments.  */
328
329 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                                        \
330 /* The flag _dl_mips_gnu_objects is set if all dynamic objects are            \
331    generated by the gnu linker. */                                            \
332 int _dl_mips_gnu_objects = 1;                                                 \
333                                                                               \
334 /* This is called from assembly stubs below which the compiler can't see.  */ \
335 static ElfW(Addr)                                                             \
336 __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))         \
337                   __attribute__ ((unused));                                   \
338                                                                               \
339 static ElfW(Addr)                                                             \
340 __dl_runtime_resolve (ElfW(Word) sym_index,                                   \
341                       ElfW(Word) return_address,                              \
342                       ElfW(Addr) old_gpreg,                                   \
343                       ElfW(Addr) stub_pc)                                     \
344 {                                                                             \
345   struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);     \
346   const ElfW(Sym) *const symtab                                               \
347     = (const void *) l->l_info[DT_SYMTAB]->d_un.d_ptr;                        \
348   const char *strtab                                                          \
349     = (const void *) l->l_info[DT_STRTAB]->d_un.d_ptr;                        \
350   const ElfW(Addr) *got                                                       \
351     = (const ElfW(Addr) *) l->l_info[DT_PLTGOT]->d_un.d_ptr;                  \
352   const ElfW(Word) local_gotno                                                \
353     = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;        \
354   const ElfW(Word) gotsym                                                     \
355     = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;             \
356   const ElfW(Sym) *definer;                                                   \
357   ElfW(Addr) loadbase;                                                        \
358   ElfW(Addr) funcaddr;                                                        \
359                                                                               \
360   /* Look up the symbol's run-time value.  */                                 \
361   definer = &symtab[sym_index];                                               \
362                                                                               \
363   loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer,          \
364                                 l->l_scope, l->l_name,                        \
365                                 ELF_MACHINE_RELOC_NOPLT);                     \
366                                                                               \
367   /* Apply the relocation with that value.  */                                \
368   funcaddr = loadbase + definer->st_value;                                    \
369   *(got + local_gotno + sym_index - gotsym) = funcaddr;                       \
370                                                                               \
371   return funcaddr;                                                            \
372 }                                                                             \
373                                                                               \
374 asm ("\n                                                                      \
375         .text\n                                                               \
376         .align  2\n                                                           \
377         .globl  _dl_runtime_resolve\n                                         \
378         .type   _dl_runtime_resolve,@function\n                               \
379         .ent    _dl_runtime_resolve\n                                         \
380 _dl_runtime_resolve:\n                                                        \
381         .set noreorder\n                                                      \
382         # Save slot call pc.\n                                                \
383         move    $3, $31\n                                                     \
384         # Modify t9 ($25) so as to point .cpload instruction.\n               \
385         addu    $25,8\n                                                       \
386         # Compute GP.\n                                                       \
387         .cpload $25\n                                                         \
388         .set reorder\n                                                        \
389         # Save slot call pc.\n                                                \
390         move    $2, $31\n                                                     \
391         # Save arguments and sp value in stack.\n                             \
392         subu    $29, 40\n                                                     \
393         .cprestore 32\n                                                       \
394         sw      $15, 36($29)\n                                                \
395         sw      $4, 12($29)\n                                                 \
396         sw      $5, 16($29)\n                                                 \
397         sw      $6, 20($29)\n                                                 \
398         sw      $7, 24($29)\n                                                 \
399         sw      $16, 28($29)\n                                                \
400         move    $16, $29\n                                                    \
401         move    $4, $24\n                                                     \
402         move    $5, $15\n                                                     \
403         move    $6, $3\n                                                      \
404         move    $7, $2\n                                                      \
405         jal     __dl_runtime_resolve\n                                        \
406         move    $29, $16\n                                                    \
407         lw      $31, 36($29)\n                                                \
408         lw      $4, 12($29)\n                                                 \
409         lw      $5, 16($29)\n                                                 \
410         lw      $6, 20($29)\n                                                 \
411         lw      $7, 24($29)\n                                                 \
412         lw      $16, 28($29)\n                                                \
413         addu    $29, 40\n                                                     \
414         move    $25, $2\n                                                     \
415         jr      $25\n                                                         \
416         .end    _dl_runtime_resolve\n                                         \
417         .previous\n                                                           \
418 ");
419
420 /* Mask identifying addresses reserved for the user program,
421    where the dynamic linker should not map anything.  */
422 #define ELF_MACHINE_USER_ADDRESS_MASK   0x80000000UL
423
424
425
426 /* Initial entry point code for the dynamic linker.
427    The C function `_dl_start' is the real entry point;
428    its return value is the user program's entry point.
429    Note how we have to be careful about two things:
430
431    1) That we allocate a minimal stack of 24 bytes for
432       every function call, the MIPS ABI states that even
433       if all arguments are passed in registers the procedure
434       called can use the 16 byte area pointed to by $sp
435       when it is called to store away the arguments passed
436       to it.
437
438    2) That under Linux the entry is named __start
439       and not just plain _start.  */
440
441 #define RTLD_START asm ("\
442         .text\n"\
443 _RTLD_PROLOGUE(ENTRY_POINT)\
444 "       .globl _dl_start_user\n\
445         .set noreorder\n\
446         bltzal $0, 0f\n\
447         nop\n\
448 0:      .cpload $31\n\
449         .set reorder\n\
450         # i386 ABI book says that the first entry of GOT holds\n\
451         # the address of the dynamic structure. Though MIPS ABI\n\
452         # doesn't say nothing about this, I emulate this here.\n\
453         la $4, _DYNAMIC\n\
454         sw $4, -0x7ff0($28)\n\
455         move $4, $29\n\
456         subu $29, 16\n\
457         jal _dl_start\n\
458         addiu $29, 16\n\
459         # Get the value of label '_dl_start_user' in t9 ($25).\n\
460         la $25, _dl_start_user\n\
461 _dl_start_user:\n\
462         .set noreorder\n\
463         .cpload $25\n\
464         .set reorder\n\
465         move $16, $28\n\
466         # Save the user entry point address in saved register.\n\
467         move $17, $2\n\
468         # See if we were run as a command with the executable file\n\
469         # name as an extra leading argument.\n\
470         lw $2, _dl_skip_args\n\
471         beq $2, $0, 1f\n\
472         # Load the original argument count.\n\
473         lw $4, 0($29)\n\
474         # Subtract _dl_skip_args from it.\n\
475         subu $4, $2\n\
476         # Adjust the stack pointer to skip _dl_skip_args words.\n\
477         sll $2,2\n\
478         addu $29, $2\n\
479         # Save back the modified argument count.\n\
480         sw $4, 0($29)\n\
481         # Get _dl_default_scope[2] as argument in _dl_init_next call below.\n\
482 1:      la $2, _dl_default_scope\n\
483         lw $4, 8($2)\n\
484         # Call _dl_init_next to return the address of an initializer\n\
485         # function to run.\n\
486         subu $29, 16\n\
487         jal _dl_init_next\n\
488         addiu $29, 16\n\
489         move $28, $16\n\
490         # Check for zero return,  when out of initializers.\n\
491         beq $2, $0, 2f\n\
492         # Call the shared object initializer function.\n\
493         move $25, $2\n\
494         lw $4, 0($29)\n\
495         lw $5, 4($29)\n\
496         lw $6, 8($29)\n\
497         lw $7, 12($29)\n\
498         jalr $25\n\
499         move $28, $16\n\
500         # Loop to call _dl_init_next for the next initializer.\n\
501         b 1b\n\
502 2:      # Clear the startup flag.  Assumes 32 bit ints.\n\
503         sw $0, _dl_starting_up\n\
504         # Pass our finalizer function to the user in ra.\n\
505         la $31, _dl_fini\n\
506         # Jump to the user entry point.\n\
507         move $25, $17\n\
508         lw $4, 0($29)\n\
509         lw $5, 4($29)\n\
510         lw $6, 8($29)\n\
511         lw $7, 12($29)\n\
512         jr $25\n"\
513 _RTLD_EPILOGUE(ENTRY_POINT)\
514         "\n.previous"\
515 );
516
517 /* The MIPS never uses Elfxx_Rela relocations.  */
518 #define ELF_MACHINE_NO_RELA 1
519
520 #endif /* !dl_machine_h */
521
522 #ifdef RESOLVE
523
524 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
525    MAP is the object containing the reloc.  */
526
527 static inline void
528 elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
529                  const ElfW(Sym) *sym, const struct r_found_version *version,
530                  ElfW(Addr) *const reloc_addr)
531 {
532   ElfW(Addr) loadbase;
533   ElfW(Addr) undo __attribute__ ((unused));
534
535   switch (ELFW(R_TYPE) (reloc->r_info))
536     {
537     case R_MIPS_REL32:
538       {
539         ElfW(Addr) undo = 0;
540
541         if (ELFW(ST_BIND) (sym->st_info) == STB_LOCAL
542             && (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION
543                 || ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE))
544           {
545             *reloc_addr += map->l_addr;
546             break;
547           }
548 #ifndef RTLD_BOOTSTRAP
549         /* This is defined in rtld.c, but nowhere in the static libc.a;
550            make the reference weak so static programs can still link.  This
551            declaration cannot be done when compiling rtld.c (i.e.  #ifdef
552            RTLD_BOOTSTRAP) because rtld.c contains the common defn for
553            _dl_rtld_map, which is incompatible with a weak decl in the same
554            file.  */
555         weak_extern (_dl_rtld_map);
556         if (map == &_dl_rtld_map)
557           /* Undo the relocation done here during bootstrapping.  Now we will
558              relocate it anew, possibly using a binding found in the user
559              program or a loaded library rather than the dynamic linker's
560              built-in definitions used while loading those libraries.  */
561           undo = map->l_addr + sym->st_value;
562 #endif
563           loadbase = RESOLVE (&sym, version, 0);
564           *reloc_addr += (sym ? (loadbase + sym->st_value) : 0) - undo;
565         }
566       break;
567     case R_MIPS_NONE:           /* Alright, Wilbur.  */
568       break;
569     default:
570       assert (! "unexpected dynamic reloc type");
571       break;
572     }
573 }
574
575 static inline void
576 elf_machine_lazy_rel (ElfW(Addr) l_addr, const ElfW(Rel) *reloc)
577 {
578   /* Do nothing.  */
579 }
580
581 #endif /* RESOLVE */