9e158c98655a8bdcdc82efebea7dd7749c2aa31b
[kopensolaris-gnu/glibc.git] / sysdeps / powerpc / dl-machine.c
1 /* Machine-dependent ELF dynamic relocation functions.  PowerPC version.
2    Copyright (C) 1995, 1996, 1997, 1998, 1999 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 not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <unistd.h>
21 #include <string.h>
22 #include <sys/param.h>
23 #include <link.h>
24 #include <dl-machine.h>
25 #include <elf/ldsodefs.h>
26 #include <elf/dynamic-link.h>
27
28 /* Because ld.so is now versioned, these functions can be in their own file;
29    no relocations need to be done to call them.
30    Of course, if ld.so is not versioned...  */
31 #if !(DO_VERSIONING - 0)
32 #error This will not work with versioning turned off, sorry.
33 #endif
34
35
36 /* stuff for the PLT */
37 #define PLT_INITIAL_ENTRY_WORDS 18
38 #define PLT_LONGBRANCH_ENTRY_WORDS 10
39 #define PLT_DOUBLE_SIZE (1<<13)
40 #define PLT_ENTRY_START_WORDS(entry_number) \
41   (PLT_INITIAL_ENTRY_WORDS + (entry_number)*2 + \
42    ((entry_number) > PLT_DOUBLE_SIZE ? \
43     ((entry_number) - PLT_DOUBLE_SIZE)*2 : \
44     0))
45 #define PLT_DATA_START_WORDS(num_entries) PLT_ENTRY_START_WORDS(num_entries)
46
47 #define OPCODE_ADDI(rd,ra,simm) \
48   (0x38000000 | (rd) << 21 | (ra) << 16 | ((simm) & 0xffff))
49 #define OPCODE_ADDIS(rd,ra,simm) \
50   (0x3c000000 | (rd) << 21 | (ra) << 16 | ((simm) & 0xffff))
51 #define OPCODE_ADD(rd,ra,rb) \
52   (0x7c000214 | (rd) << 21 | (ra) << 16 | (rb) << 11)
53 #define OPCODE_B(target) (0x48000000 | ((target) & 0x03fffffc))
54 #define OPCODE_BA(target) (0x48000002 | ((target) & 0x03fffffc))
55 #define OPCODE_BCTR() 0x4e800420
56 #define OPCODE_LWZ(rd,d,ra) \
57   (0x80000000 | (rd) << 21 | (ra) << 16 | ((d) & 0xffff))
58 #define OPCODE_MTCTR(rd) (0x7C0903A6 | (rd) << 21)
59 #define OPCODE_RLWINM(ra,rs,sh,mb,me) \
60   (0x54000000 | (rs) << 21 | (ra) << 16 | (sh) << 11 | (mb) << 6 | (me) << 1)
61
62 #define OPCODE_LI(rd,simm)    OPCODE_ADDI(rd,0,simm)
63 #define OPCODE_SLWI(ra,rs,sh) OPCODE_RLWINM(ra,rs,sh,0,31-sh)
64
65
66 #define PPC_DCBST(where) asm ("dcbst 0,%0" : : "r"(where) : "memory")
67 #define PPC_SYNC asm ("sync" : : : "memory")
68 #define PPC_ISYNC asm volatile ("sync; isync" : : : "memory")
69 #define PPC_ICBI(where) asm ("icbi 0,%0" : : "r"(where) : "memory")
70 #define PPC_DIE asm volatile ("tweq 0,0")
71
72 /* Use this when you've modified some code, but it won't be in the
73    instruction fetch queue (or when it doesn't matter if it is). */
74 #define MODIFIED_CODE_NOQUEUE(where) \
75      do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); } while (0)
76 /* Use this when it might be in the instruction queue. */
77 #define MODIFIED_CODE(where) \
78      do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); PPC_ISYNC; } while (0)
79
80
81 /* The idea here is that to conform to the ABI, we are supposed to try
82    to load dynamic objects between 0x10000 (we actually use 0x40000 as
83    the lower bound, to increase the chance of a memory reference from
84    a null pointer giving a segfault) and the program's load address;
85    this may allow us to use a branch instruction in the PLT rather
86    than a computed jump.  The address is only used as a preference for
87    mmap, so if we get it wrong the worst that happens is that it gets
88    mapped somewhere else.  */
89
90 ElfW(Addr)
91 __elf_preferred_address(struct link_map *loader, size_t maplength,
92                         ElfW(Addr) mapstartpref)
93 {
94   ElfW(Addr) low, high;
95   struct link_map *l;
96
97   /* If the object has a preference, load it there!  */
98   if (mapstartpref != 0)
99     return mapstartpref;
100
101   /* Otherwise, quickly look for a suitable gap between 0x3FFFF and
102      0x70000000.  0x3FFFF is so that references off NULL pointers will
103      cause a segfault, 0x70000000 is just paranoia (it should always
104      be superceded by the program's load address).  */
105   low =  0x0003FFFF;
106   high = 0x70000000;
107   for (l = _dl_loaded; l; l = l->l_next)
108     {
109       ElfW(Addr) mapstart, mapend;
110       mapstart = l->l_map_start & ~(_dl_pagesize - 1);
111       mapend = l->l_map_end | (_dl_pagesize - 1);
112       assert (mapend > mapstart);
113
114       if (mapend >= high && high >= mapstart)
115         high = mapstart;
116       else if (mapend >= low && low >= mapstart)
117         low = mapend;
118       else if (high >= mapend && mapstart >= low)
119         {
120           if (high - mapend >= mapstart - low)
121             low = mapend;
122           else
123             high = mapstart;
124         }
125     }
126
127   high -= 0x10000; /* Allow some room between objects.  */
128   maplength = (maplength | (_dl_pagesize-1)) + 1;
129   if (high <= low || high - low < maplength )
130     return 0;
131   return high - maplength;  /* Both high and maplength are page-aligned.  */
132 }
133
134 /* Set up the loaded object described by L so its unrelocated PLT
135    entries will jump to the on-demand fixup code in dl-runtime.c.
136    Also install a small trampoline to be used by entries that have
137    been relocated to an address too far away for a single branch.  */
138
139 /* A PLT entry does one of three things:
140    (i)   Jumps to the actual routine. Such entries are set up above, in
141          elf_machine_rela.
142
143    (ii)  Jumps to the actual routine via glue at the start of the PLT.
144          We do this by putting the address of the routine in space
145          allocated at the end of the PLT, and when the PLT entry is
146          called we load the offset of that word (from the start of the
147          space) into r11, then call the glue, which loads the word and
148          branches to that address. These entries are set up in
149          elf_machine_rela, but the glue is set up here.
150
151    (iii) Loads the index of this PLT entry (we count the double-size
152          entries as one entry for this purpose) into r11, then
153          branches to code at the start of the PLT. This code then
154          calls `fixup', in dl-runtime.c, via the glue in the macro
155          ELF_MACHINE_RUNTIME_TRAMPOLINE, which resets the PLT entry to
156          be one of the above two types. These entries are set up here.  */
157 int
158 __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
159 {
160   if (map->l_info[DT_JMPREL])
161     {
162       Elf32_Word i;
163       /* Fill in the PLT. Its initial contents are directed to a
164          function earlier in the PLT which arranges for the dynamic
165          linker to be called back.  */
166       Elf32_Word *plt = (Elf32_Word *) map->l_info[DT_PLTGOT]->d_un.d_val;
167       Elf32_Word num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
168                                     / sizeof (Elf32_Rela));
169       Elf32_Word rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries);
170       Elf32_Word size_modified;
171       extern void _dl_runtime_resolve (void);
172       extern void _dl_prof_resolve (void);
173       Elf32_Word dlrr;
174
175       dlrr = (Elf32_Word)(char *)(profile
176                                   ? _dl_prof_resolve
177                                   : _dl_runtime_resolve);
178
179       if (profile && _dl_name_match_p (_dl_profile, map))
180         /* This is the object we are looking for.  Say that we really
181            want profiling and the timers are started.  */
182         _dl_profile_map = map;
183
184       if (lazy)
185         for (i = 0; i < num_plt_entries; i++)
186         {
187           Elf32_Word offset = PLT_ENTRY_START_WORDS (i);
188
189           if (i >= PLT_DOUBLE_SIZE)
190             {
191               plt[offset  ] = OPCODE_LI (11, i * 4);
192               plt[offset+1] = OPCODE_ADDIS (11, 11, (i * 4 + 0x8000) >> 16);
193               plt[offset+2] = OPCODE_B (-(4 * (offset + 2)));
194             }
195           else
196             {
197               plt[offset  ] = OPCODE_LI (11, i * 4);
198               plt[offset+1] = OPCODE_B (-(4 * (offset + 1)));
199             }
200         }
201
202       /* Multiply index of entry by 3 (in r11).  */
203       plt[0] = OPCODE_SLWI (12, 11, 1);
204       plt[1] = OPCODE_ADD (11, 12, 11);
205       if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000)
206         {
207           /* Load address of link map in r12.  */
208           plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) map);
209           plt[3] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map
210                                            + 0x8000) >> 16));
211
212           /* Call _dl_runtime_resolve.  */
213           plt[4] = OPCODE_BA (dlrr);
214         }
215       else
216         {
217           /* Get address of _dl_runtime_resolve in CTR.  */
218           plt[2] = OPCODE_LI (12, dlrr);
219           plt[3] = OPCODE_ADDIS (12, 12, (dlrr + 0x8000) >> 16);
220           plt[4] = OPCODE_MTCTR (12);
221
222           /* Load address of link map in r12.  */
223           plt[5] = OPCODE_LI (12, (Elf32_Word) (char *) map);
224           plt[6] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map
225                                            + 0x8000) >> 16));
226
227           /* Call _dl_runtime_resolve.  */
228           plt[7] = OPCODE_BCTR ();
229         }
230
231
232       /* Convert the index in r11 into an actual address, and get the
233          word at that address.  */
234       plt[PLT_LONGBRANCH_ENTRY_WORDS] =
235         OPCODE_ADDIS (11, 11, (((Elf32_Word) (char*) (plt + rel_offset_words)
236                                 + 0x8000) >> 16));
237       plt[PLT_LONGBRANCH_ENTRY_WORDS+1] =
238         OPCODE_LWZ (11, (Elf32_Word) (char*) (plt + rel_offset_words), 11);
239
240       /* Call the procedure at that address.  */
241       plt[PLT_LONGBRANCH_ENTRY_WORDS + 2] = OPCODE_MTCTR (11);
242       plt[PLT_LONGBRANCH_ENTRY_WORDS + 3] = OPCODE_BCTR ();
243
244
245       /* Now, we've modified code (quite a lot of code, possibly).  We
246          need to write the changes from the data cache to a
247          second-level unified cache, then make sure that stale data in
248          the instruction cache is removed.  (In a multiprocessor
249          system, the effect is more complex.)  Most of the PLT shouldn't
250          be in the instruction cache, but there may be a little overlap
251          at the start and the end.
252
253          Assumes the cache line size is at least 32 bytes, or at least
254          that dcbst and icbi apply to 32-byte lines. At present, all
255          PowerPC processors have line sizes of exactly 32 bytes.  */
256
257       size_modified = lazy ? rel_offset_words : PLT_INITIAL_ENTRY_WORDS;
258       for (i = 0; i < size_modified; i+= 8)
259         PPC_DCBST (plt + i);
260       PPC_DCBST (plt + size_modified - 1);
261       PPC_SYNC;
262       PPC_ICBI (plt);
263       PPC_ICBI (plt + size_modified-1);
264       PPC_ISYNC;
265     }
266
267   return lazy;
268 }
269
270 void
271 __elf_machine_fixup_plt(struct link_map *map, const Elf32_Rela *reloc,
272                         Elf32_Addr *reloc_addr, Elf32_Addr finaladdr)
273 {
274   Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
275   if (delta << 6 >> 6 == delta)
276     *reloc_addr = OPCODE_B (delta);
277   else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000)
278     *reloc_addr = OPCODE_BA (finaladdr);
279   else
280     {
281       Elf32_Word *plt;
282       Elf32_Word index;
283
284       plt = (Elf32_Word *) map->l_info[DT_PLTGOT]->d_un.d_val;
285       index = (reloc_addr - plt - PLT_INITIAL_ENTRY_WORDS)/2;
286       if (index >= PLT_DOUBLE_SIZE)
287         {
288           /* Slots greater than or equal to 2^13 have 4 words available
289              instead of two.  */
290           /* FIXME: There are some possible race conditions in this code,
291              when called from 'fixup'.
292
293              1) Suppose that a lazy PLT entry is executing, a context switch
294              between threads (or a signal) occurs, and the new thread or
295              signal handler calls the same lazy PLT entry.  Then the PLT entry
296              would be changed while it's being run, which will cause a segfault
297              (almost always).
298
299              2) Suppose the reverse: that a lazy PLT entry is being updated,
300              a context switch occurs, and the new code calls the lazy PLT
301              entry that is being updated.  Then the half-fixed PLT entry will
302              be executed, which will also almost always cause a segfault.
303
304              These problems don't happen with the 2-word entries, because
305              only one of the two instructions are changed when a lazy entry
306              is retargeted at the actual PLT entry; the li instruction stays
307              the same (we have to update it anyway, because we might not be
308              updating a lazy PLT entry).  */
309
310           reloc_addr[0] = OPCODE_LI (11, finaladdr);
311           reloc_addr[1] = OPCODE_ADDIS (11, 11, (finaladdr + 0x8000) >> 16);
312           reloc_addr[2] = OPCODE_MTCTR (11);
313           reloc_addr[3] = OPCODE_BCTR ();
314         }
315       else
316         {
317           Elf32_Word num_plt_entries;
318
319           num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
320                              / sizeof(Elf32_Rela));
321
322           plt[index+PLT_DATA_START_WORDS (num_plt_entries)] = finaladdr;
323           reloc_addr[0] = OPCODE_LI (11, index*4);
324           reloc_addr[1] = OPCODE_B (-(4*(index*2
325                                          + 1
326                                          - PLT_LONGBRANCH_ENTRY_WORDS
327                                          + PLT_INITIAL_ENTRY_WORDS)));
328           reloc_addr += 1;  /* This is the modified address.  */
329         }
330     }
331   MODIFIED_CODE (reloc_addr);
332 }
333
334 void
335 __process_machine_rela (struct link_map *map,
336                         const Elf32_Rela *reloc,
337                         const Elf32_Sym *sym,
338                         const Elf32_Sym *refsym,
339                         Elf32_Addr *const reloc_addr,
340                         Elf32_Addr const finaladdr,
341                         int rinfo)
342 {
343   switch (rinfo)
344     {
345     case R_PPC_NONE:
346       return;
347
348     case R_PPC_ADDR32:
349     case R_PPC_UADDR32:
350     case R_PPC_GLOB_DAT:
351     case R_PPC_RELATIVE:
352       *reloc_addr = finaladdr;
353       return;
354
355     case R_PPC_ADDR24:
356       if (finaladdr > 0x01fffffc && finaladdr < 0xfe000000)
357         _dl_signal_error (0, map->l_name,
358                           "R_PPC_ADDR24 relocation out of range");
359       *reloc_addr = (*reloc_addr & 0xfc000003) | (finaladdr & 0x3fffffc);
360       break;
361
362     case R_PPC_ADDR16:
363     case R_PPC_UADDR16:
364       if (finaladdr > 0x7fff && finaladdr < 0x8000)
365         _dl_signal_error (0, map->l_name,
366                           "R_PPC_ADDR16 relocation out of range");
367       *(Elf32_Half*) reloc_addr = finaladdr;
368       break;
369
370     case R_PPC_ADDR16_LO:
371       *(Elf32_Half*) reloc_addr = finaladdr;
372       break;
373
374     case R_PPC_ADDR16_HI:
375       *(Elf32_Half*) reloc_addr = finaladdr >> 16;
376       break;
377
378     case R_PPC_ADDR16_HA:
379       *(Elf32_Half*) reloc_addr = (finaladdr + 0x8000) >> 16;
380       break;
381
382     case R_PPC_ADDR14:
383     case R_PPC_ADDR14_BRTAKEN:
384     case R_PPC_ADDR14_BRNTAKEN:
385       if (finaladdr > 0x7fff && finaladdr < 0x8000)
386         _dl_signal_error (0, map->l_name,
387                           "R_PPC_ADDR14 relocation out of range");
388       *reloc_addr = (*reloc_addr & 0xffff0003) | (finaladdr & 0xfffc);
389       if (rinfo != R_PPC_ADDR14)
390         *reloc_addr = ((*reloc_addr & 0xffdfffff)
391                        | ((rinfo == R_PPC_ADDR14_BRTAKEN)
392                           ^ (finaladdr >> 31)) << 21);
393       break;
394
395     case R_PPC_REL24:
396       {
397         Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
398         if (delta << 6 >> 6 != delta)
399           _dl_signal_error (0, map->l_name,
400                             "R_PPC_REL24 relocation out of range");
401         *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
402       }
403       break;
404
405     case R_PPC_COPY:
406       if (sym == NULL)
407         /* This can happen in trace mode when an object could not be
408            found.  */
409         return;
410       if (sym->st_size > refsym->st_size
411           || (_dl_verbose && sym->st_size < refsym->st_size))
412         {
413           const char *strtab;
414
415           strtab = (const void *) map->l_info[DT_STRTAB]->d_un.d_ptr;
416           _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
417                             ": Symbol `", strtab + refsym->st_name,
418                             "' has different size in shared object, "
419                             "consider re-linking\n", NULL);
420         }
421       memcpy (reloc_addr, (char *) finaladdr, MIN (sym->st_size,
422                                                    refsym->st_size));
423       return;
424
425     case R_PPC_REL32:
426       *reloc_addr = finaladdr - (Elf32_Word) (char *) reloc_addr;
427       return;
428
429     case R_PPC_JMP_SLOT:
430       elf_machine_fixup_plt (map, reloc, reloc_addr, finaladdr);
431       return;
432
433     default:
434       _dl_reloc_bad_type (map, rinfo, 0);
435       return;
436     }
437
438   MODIFIED_CODE_NOQUEUE (reloc_addr);
439 }