Update.
[platform/upstream/glibc.git] / sysdeps / powerpc / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  PowerPC version.
2    Copyright (C) 1995, 1996, 1997 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 #ifndef dl_machine_h
21 #define dl_machine_h
22
23 #define ELF_MACHINE_NAME "powerpc"
24
25 #include <assert.h>
26 #include <string.h>
27 #include <link.h>
28 #include <sys/param.h>
29
30
31 /* stuff for the PLT */
32 #define PLT_INITIAL_ENTRY_WORDS 18
33 #define PLT_LONGBRANCH_ENTRY_WORDS 10
34 #define PLT_DOUBLE_SIZE (1<<13)
35 #define PLT_ENTRY_START_WORDS(entry_number) \
36   (PLT_INITIAL_ENTRY_WORDS + (entry_number)*2 + \
37    ((entry_number) > PLT_DOUBLE_SIZE ? \
38     ((entry_number) - PLT_DOUBLE_SIZE)*2 : \
39     0))
40 #define PLT_DATA_START_WORDS(num_entries) PLT_ENTRY_START_WORDS(num_entries)
41
42 #define OPCODE_ADDI(rd,ra,simm) \
43   (0x38000000 | (rd) << 21 | (ra) << 16 | (simm) & 0xffff)
44 #define OPCODE_ADDIS(rd,ra,simm) \
45   (0x3c000000 | (rd) << 21 | (ra) << 16 | (simm) & 0xffff)
46 #define OPCODE_ADD(rd,ra,rb) \
47   (0x7c000214 | (rd) << 21 | (ra) << 16 | (rb) << 11)
48 #define OPCODE_B(target) (0x48000000 | (target) & 0x03fffffc)
49 #define OPCODE_BA(target) (0x48000002 | (target) & 0x03fffffc)
50 #define OPCODE_BCTR() 0x4e800420
51 #define OPCODE_LWZ(rd,d,ra) \
52   (0x80000000 | (rd) << 21 | (ra) << 16 | (d) & 0xffff)
53 #define OPCODE_MTCTR(rd) (0x7C0903A6 | (rd) << 21)
54 #define OPCODE_RLWINM(ra,rs,sh,mb,me) \
55   (0x54000000 | (rs) << 21 | (ra) << 16 | (sh) << 11 | (mb) << 6 | (me) << 1)
56
57 #define OPCODE_LI(rd,simm)    OPCODE_ADDI(rd,0,simm)
58 #define OPCODE_SLWI(ra,rs,sh) OPCODE_RLWINM(ra,rs,sh,0,31-sh)
59
60 #define PPC_DCBST(where) asm volatile ("dcbst 0,%0" : : "r"(where))
61 #define PPC_SYNC asm volatile ("sync")
62 #define PPC_ISYNC asm volatile ("sync; isync")
63 #define PPC_ICBI(where) asm volatile ("icbi 0,%0" : : "r"(where))
64 #define PPC_DIE asm volatile ("tweq 0,0")
65
66 /* Use this when you've modified some code, but it won't be in the
67    instruction fetch queue (or when it doesn't matter if it is). */
68 #define MODIFIED_CODE_NOQUEUE(where) \
69      do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); } while (0)
70 /* Use this when it might be in the instruction queue. */
71 #define MODIFIED_CODE(where) \
72      do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); PPC_ISYNC; } while (0)
73
74
75 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
76 static inline int
77 elf_machine_matches_host (Elf32_Half e_machine)
78 {
79   return e_machine == EM_PPC;
80 }
81
82
83 /* Return the link-time address of _DYNAMIC, stored as
84    the first value in the GOT. */
85 static inline Elf32_Addr
86 elf_machine_dynamic (void)
87 {
88   Elf32_Addr *got;
89   asm (" bl _GLOBAL_OFFSET_TABLE_-4@local"
90        : "=l"(got));
91   return *got;
92 }
93
94 /* Return the run-time load address of the shared object.  */
95 static inline Elf32_Addr
96 elf_machine_load_address (void)
97 {
98   unsigned *got;
99   unsigned *branchaddr;
100
101   /* This is much harder than you'd expect.  Possibly I'm missing something.
102      The 'obvious' way:
103
104        Apparently, "bcl 20,31,$+4" is what should be used to load LR
105        with the address of the next instruction.
106        I think this is so that machines that do bl/blr pairing don't
107        get confused.
108
109      asm ("bcl 20,31,0f ;"
110           "0: mflr 0 ;"
111           "lis %0,0b@ha;"
112           "addi %0,%0,0b@l;"
113           "subf %0,%0,0"
114           : "=b" (addr) : : "r0", "lr");
115
116      doesn't work, because the linker doesn't have to (and in fact doesn't)
117      update the @ha and @l references; the loader (which runs after this
118      code) will do that.
119
120      Instead, we use the following trick:
121
122      The linker puts the _link-time_ address of _DYNAMIC at the first
123      word in the GOT. We could branch to that address, if we wanted,
124      by using an @local reloc; the linker works this out, so it's safe
125      to use now. We can't, of course, actually branch there, because
126      we'd cause an illegal instruction exception; so we need to compute
127      the address ourselves. That gives us the following code: */
128
129   /* Get address of the 'b _DYNAMIC@local'...  */
130   asm ("bl 0f ;"
131        "b _DYNAMIC@local;"
132        "0:"
133        : "=l"(branchaddr));
134
135   /* ... and the address of the GOT.  */
136   asm (" bl _GLOBAL_OFFSET_TABLE_-4@local"
137        : "=l"(got));
138
139   /* So now work out the difference between where the branch actually points,
140      and the offset of that location in memory from the start of the file.  */
141   return ((Elf32_Addr)branchaddr - *got
142           + (*branchaddr & 0x3fffffc
143              | (int)(*branchaddr << 6 & 0x80000000) >> 6));
144 }
145
146 #define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) /* nothing */
147
148 /* The PLT uses Elf32_Rela relocs.  */
149 #define elf_machine_relplt elf_machine_rela
150
151 /* This code is used in dl-runtime.c to call the `fixup' function
152    and then redirect to the address it returns.  It is called
153    from code built in the PLT by elf_machine_runtime_setup.  */
154 #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
155         .section \".text\"
156         .align 2
157         .globl _dl_runtime_resolve
158         .type _dl_runtime_resolve,@function
159 _dl_runtime_resolve:
160  # We need to save the registers used to pass parameters, and register 0,
161  # which is used by _mcount; the registers are saved in a stack frame.
162         stwu 1,-48(1)
163         stw 0,12(1)
164         stw 3,16(1)
165         stw 4,20(1)
166  # The code that calls this has put parameters for `fixup' in r12 and r11.
167         mr 3,12
168         stw 5,24(1)
169         mr 4,11
170         stw 6,28(1)
171         mflr 0
172  # We also need to save some of the condition register fields.
173         stw 7,32(1)
174         stw 0,52(1)
175         stw 8,36(1)
176         mfcr 0
177         stw 9,40(1)
178         stw 10,44(1)
179         stw 0,8(1)
180         bl fixup@local
181  # 'fixup' returns the address we want to branch to.
182         mtctr 3
183  # Put the registers back...
184         lwz 0,52(1)
185         lwz 10,44(1)
186         lwz 9,40(1)
187         mtlr 0
188         lwz 8,36(1)
189         lwz 0,8(1)
190         lwz 7,32(1)
191         lwz 6,28(1)
192         mtcrf 0xFF,0
193         lwz 5,24(1)
194         lwz 4,20(1)
195         lwz 3,16(1)
196         lwz 0,12(1)
197  # ...unwind the stack frame, and jump to the PLT entry we updated.
198         addi 1,1,48
199         bctr
200 0:
201         .size    _dl_runtime_resolve,0b-_dl_runtime_resolve
202  # Undo '.section text'.
203         .previous
204 ");
205
206 /* Initial entry point code for the dynamic linker.
207    The C function `_dl_start' is the real entry point;
208    its return value is the user program's entry point.  */
209 #define RTLD_START \
210 static ElfW(Addr) _dl_start (void *arg) __attribute__((unused)); \
211 asm ("\
212         .section \".text\"
213         .align 2
214         .globl _start
215         .type _start,@function
216 _start:
217  # We start with the following on the stack, from top:
218  # argc (4 bytes);
219  # arguments for program (terminated by NULL);
220  # environment variables (terminated by NULL);
221  # arguments for the program loader.
222  # FIXME: perhaps this should do the same trick as elf/start.c?
223
224  # Call _dl_start with one parameter pointing at argc
225         mr   3,1
226  #  (we have to frob the stack pointer a bit to allow room for
227  #   _dl_start to save the link register)
228         li   4,0
229         addi 1,1,-16
230         stw  4,0(1)
231         bl   _dl_start@local
232
233  # Now, we do our main work of calling initialisation procedures.
234  # The ELF ABI doesn't say anything about parameters for these,
235  # so we just pass argc, argv, and the environment.
236  # Changing these is strongly discouraged (not least because argc is
237  # passed by value!).
238
239  #  Put our GOT pointer in r31,
240         bl   _GLOBAL_OFFSET_TABLE_-4@local
241         mflr 31
242  #  the address of _start in r30,
243         mr   30,3
244  #  &_dl_argc in 29, &_dl_argv in 27, and _dl_default_scope in 28.
245         lwz  28,_dl_default_scope@got(31)
246         lwz  29,_dl_argc@got(31)
247         lwz  27,_dl_argv@got(31)
248 0:
249  #  Set initfunc = _dl_init_next(_dl_default_scope[2])
250         lwz  3,8(28)
251         bl   _dl_init_next@plt
252  # If initfunc is NULL, we exit the loop; otherwise,
253         cmpwi 3,0
254         beq  1f
255  # call initfunc(_dl_argc, _dl_argv, _dl_argv+_dl_argc+1)
256         mtlr 3
257         lwz  3,0(29)
258         lwz  4,0(27)
259         slwi 5,3,2
260         add  5,4,5
261         addi 5,5,4
262         blrl
263  # and loop.
264         b    0b
265 1:
266  # Now, to conform to the ELF ABI, we have to:
267  # Pass argc (actually _dl_argc) in r3;
268         lwz  3,0(29)
269  # pass argv (actually _dl_argv) in r4;
270         lwz  4,0(27)
271  # pass envp (actually _dl_argv+_dl_argc+1) in r5;
272         slwi 5,3,2
273         add  6,4,5
274         addi 5,6,4
275  # pass the auxilary vector in r6. This is passed to us just after _envp.
276 2:      lwzu 0,4(6)
277         cmpwi 0,0,0
278         bne  2b
279         addi 6,6,4
280  # Pass a termination function pointer (in this case _dl_fini) in r7.
281         lwz  7,_dl_fini@got(31)
282  # Now, call the start function in r30...
283         mtctr 30
284         lwz  26,_dl_starting_up@got(31)
285  # Pass the stack pointer in r1 (so far so good), pointing to a NULL value.
286  # (This lets our startup code distinguish between a program linked statically,
287  # which linux will call with argc on top of the stack which will hopefully
288  # never be zero, and a dynamically linked program which will always have
289  # a NULL on the top of the stack).
290  # Take the opportunity to clear LR, so anyone who accidentally returns
291  # from _start gets SEGV.  Also clear the next few words of the stack.
292         li   31,0
293         stw  31,0(1)
294         mtlr 31
295         stw  31,4(1)
296         stw  31,8(1)
297         stw  31,12(1)
298  # Clear _dl_starting_up.
299         stw  31,0(26)
300  # Go do it!
301         bctr
302 0:
303         .size    _start,0b-_start
304  # Undo '.section text'.
305         .previous
306 ");
307
308 /* The idea here is that to conform to the ABI, we are supposed to try
309    to load dynamic objects between 0x10000 (we actually use 0x40000 as
310    the lower bound, to increase the chance of a memory reference from
311    a null pointer giving a segfault) and the program's load address.
312    Regrettably, in this code we can't find the program's load address,
313    so we punt and choose 0x01800000, which is below the ABI's
314    recommended default, and what GNU ld currently chooses. We only use
315    the address as a preference for mmap, so if we get it wrong the
316    worst that happens is that it gets mapped somewhere else.
317
318    FIXME: Unfortunately, 'somewhere else' is probably right after the
319    program's break, which causes malloc to fail.  We really need more
320    information here about the way memory is mapped.  */
321
322 #define ELF_PREFERRED_ADDRESS_DATA                                            \
323 static ElfW(Addr) _dl_preferred_address = 1
324
325 #define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref)                \
326 ( {                                                                           \
327    ElfW(Addr) prefd;                                                          \
328    if (mapstartpref != 0 && _dl_preferred_address == 1)                       \
329      _dl_preferred_address = mapstartpref;                                    \
330    if (mapstartpref != 0)                                                     \
331      prefd = mapstartpref;                                                    \
332    else if (_dl_preferred_address == 1)                                       \
333      prefd = _dl_preferred_address =                                          \
334           (0x01800000 - maplength - 0x10000) &                                \
335            ~(_dl_pagesize - 1);                                               \
336    else if (_dl_preferred_address < maplength + 0x50000)                      \
337      prefd = 0;                                                               \
338    else                                                                       \
339      prefd = _dl_preferred_address =                                          \
340        ((_dl_preferred_address - maplength - 0x10000)                         \
341         & ~(_dl_pagesize - 1));                                               \
342    prefd;                                                                     \
343 } )
344
345 #define ELF_FIXED_ADDRESS(loader, mapstart)                                   \
346 ( {                                                                           \
347    if (mapstart != 0 && _dl_preferred_address == 1)                           \
348      _dl_preferred_address = mapstart;                                        \
349 } )
350
351 /* We require the address of the PLT entry returned from fixup, not
352    the first word of the PLT entry. */
353 #define ELF_FIXUP_RETURN_VALUE(map, result)  ((Elf32_Addr) &(result))
354
355 /* Nonzero iff TYPE should not be allowed to resolve to one of
356    the main executable's symbols, as for a COPY reloc.  */
357 #define elf_machine_lookup_noexec_p(type) ((type) == R_PPC_COPY)
358
359 /* Nonzero iff TYPE describes relocation of a PLT entry, so
360    PLT entries should not be allowed to define the value.  */
361 /* We never want to use a PLT entry as the destination of a
362    reloc, when what is being relocated is a branch. This is
363    partly for efficiency, but mostly so we avoid loops.  */
364 #define elf_machine_lookup_noplt_p(type) ((type) == R_PPC_REL24 ||            \
365                                           (type) == R_PPC_ADDR24 ||           \
366                                           (type) == R_PPC_JMP_SLOT)
367
368 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
369 #define ELF_MACHINE_RELOC_NOPLT R_PPC_JMP_SLOT
370
371 /* Nonzero iff TYPE describes relocation of a PLT entry, so
372    PLT entries should not be allowed to define the value.  */
373 #define elf_machine_pltrel_p(type) ((type) == R_PPC_JMP_SLOT)
374
375 /* Set up the loaded object described by L so its unrelocated PLT
376    entries will jump to the on-demand fixup code in dl-runtime.c.
377    Also install a small trampoline to be used by entries that have
378    been relocated to an address too far away for a single branch.  */
379
380 /* A PLT entry does one of three things:
381    (i)   Jumps to the actual routine. Such entries are set up above, in
382          elf_machine_rela.
383
384    (ii)  Jumps to the actual routine via glue at the start of the PLT.
385          We do this by putting the address of the routine in space
386          allocated at the end of the PLT, and when the PLT entry is
387          called we load the offset of that word (from the start of the
388          space) into r11, then call the glue, which loads the word and
389          branches to that address. These entries are set up in
390          elf_machine_rela, but the glue is set up here.
391
392    (iii) Loads the index of this PLT entry (we count the double-size
393          entries as one entry for this purpose) into r11, then
394          branches to code at the start of the PLT. This code then
395          calls `fixup', in dl-runtime.c, via the glue in the macro
396          ELF_MACHINE_RUNTIME_TRAMPOLINE, which resets the PLT entry to
397          be one of the above two types. These entries are set up here.  */
398 static inline int
399 elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
400 {
401   if (map->l_info[DT_JMPREL])
402     {
403       Elf32_Word i;
404       /* Fill in the PLT. Its initial contents are directed to a
405          function earlier in the PLT which arranges for the dynamic
406          linker to be called back.  */
407       Elf32_Word *plt = (Elf32_Word *) ((char *) map->l_addr
408                                         + map->l_info[DT_PLTGOT]->d_un.d_val);
409       Elf32_Word num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
410                                     / sizeof (Elf32_Rela));
411       Elf32_Word rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries);
412       extern void _dl_runtime_resolve (void);
413       Elf32_Word size_modified;
414
415       if (lazy)
416         for (i = 0; i < num_plt_entries; i++)
417         {
418           Elf32_Word offset = PLT_ENTRY_START_WORDS (i);
419
420           if (i >= PLT_DOUBLE_SIZE)
421             {
422               plt[offset  ] = OPCODE_LI (11, i * 4);
423               plt[offset+1] = OPCODE_ADDIS (11, 11, (i * 4 + 0x8000) >> 16);
424               plt[offset+2] = OPCODE_B (-(4 * (offset + 2)));
425             }
426           else
427             {
428               plt[offset  ] = OPCODE_LI (11, i * 4);
429               plt[offset+1] = OPCODE_B (-(4 * (offset + 1)));
430             }
431         }
432
433       /* Multiply index of entry by 3 (in r11).  */
434       plt[0] = OPCODE_SLWI (12, 11, 1);
435       plt[1] = OPCODE_ADD (11, 12, 11);
436       if ((Elf32_Word) (char *) _dl_runtime_resolve <= 0x01fffffc ||
437           (Elf32_Word) (char *) _dl_runtime_resolve >= 0xfe000000)
438         {
439           /* Load address of link map in r12.  */
440           plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) map);
441           plt[3] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map
442                                            + 0x8000) >> 16));
443
444           /* Call _dl_runtime_resolve.  */
445           plt[4] = OPCODE_BA ((Elf32_Word) (char *) _dl_runtime_resolve);
446         }
447       else
448         {
449           /* Get address of _dl_runtime_resolve in CTR.  */
450           plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) _dl_runtime_resolve);
451           plt[3] = OPCODE_ADDIS (12, 12, ((((Elf32_Word) (char *)
452                                             _dl_runtime_resolve)
453                                            + 0x8000) >> 16));
454           plt[4] = OPCODE_MTCTR (12);
455
456           /* Load address of link map in r12.  */
457           plt[5] = OPCODE_LI (12, (Elf32_Word) (char *) map);
458           plt[6] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map
459                                            + 0x8000) >> 16));
460
461           /* Call _dl_runtime_resolve.  */
462           plt[7] = OPCODE_BCTR ();
463         }
464
465
466       /* Convert the index in r11 into an actual address, and get the
467          word at that address.  */
468       plt[PLT_LONGBRANCH_ENTRY_WORDS] =
469         OPCODE_ADDIS (11, 11, (((Elf32_Word) (char*) (plt + rel_offset_words)
470                                 + 0x8000) >> 16));
471       plt[PLT_LONGBRANCH_ENTRY_WORDS+1] =
472         OPCODE_LWZ (11, (Elf32_Word) (char*) (plt+rel_offset_words), 11);
473
474       /* Call the procedure at that address.  */
475       plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR (11);
476       plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR ();
477
478
479       /* Now, we've modified code (quite a lot of code, possibly).  We
480          need to write the changes from the data cache to a
481          second-level unified cache, then make sure that stale data in
482          the instruction cache is removed.  (In a multiprocessor
483          system, the effect is more complex.)
484
485          Assumes the cache line size is at least 32 bytes, or at least
486          that dcbst and icbi apply to 32-byte lines. At present, all
487          PowerPC processors have line sizes of exactly 32 bytes.  */
488
489       size_modified = lazy ? rel_offset_words : PLT_INITIAL_ENTRY_WORDS;
490       for (i = 0; i < size_modified; i+=8)
491         PPC_DCBST (plt + i);
492       PPC_SYNC;
493       for (i = 0; i < size_modified; i+=8)
494         PPC_ICBI (plt + i);
495       PPC_ISYNC;
496     }
497
498   return lazy;
499 }
500
501 static inline void
502 elf_machine_lazy_rel (struct link_map *map, const Elf32_Rela *reloc)
503 {
504   assert (ELF32_R_TYPE (reloc->r_info) == R_PPC_JMP_SLOT);
505   /* elf_machine_runtime_setup handles this. */
506 }
507
508 #endif /* dl_machine_h */
509
510 #ifdef RESOLVE
511
512 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
513    LOADADDR is the load address of the object; INFO is an array indexed
514    by DT_* of the .dynamic section info.  */
515
516 static inline void
517 elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
518                   const Elf32_Sym *sym, const struct r_found_version *version,
519                   Elf32_Addr *const reloc_addr)
520 {
521 #ifndef RTLD_BOOTSTRAP
522   const Elf32_Sym *const refsym = sym;
523   extern char **_dl_argv;
524 #endif
525   Elf32_Word loadbase, finaladdr;
526   const int rinfo = ELF32_R_TYPE (reloc->r_info);
527
528   if (rinfo == R_PPC_NONE)
529     return;
530
531   assert (sym != NULL);
532   /* The condition on the next two lines is a hack around a bug in Solaris
533      tools on Sparc.  It's not clear whether it should really be here at all,
534      but if not the binutils need to be changed.  */
535   if ((sym->st_shndx != SHN_UNDEF
536        && ELF32_ST_BIND (sym->st_info) == STB_LOCAL)
537       || rinfo == R_PPC_RELATIVE)
538     {
539       /* Has already been relocated.  */
540       loadbase = map->l_addr;
541       finaladdr = loadbase + reloc->r_addend;
542     }
543   else
544     {
545       loadbase = (Elf32_Word) (char *) (RESOLVE (&sym, version,
546                                                  ELF32_R_TYPE(reloc->r_info)));
547       if (sym == NULL)
548         {
549           /* Weak symbol that wasn't actually defined anywhere.  */
550           assert(loadbase == 0);
551           finaladdr = reloc->r_addend;
552         }
553       else
554         finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value
555                      + reloc->r_addend);
556     }
557
558   /* This is still an if/else if chain because GCC uses the GOT to find
559      the table for table-based switch statements, and we haven't set it
560      up yet.  */
561   if (rinfo == R_PPC_UADDR32 ||
562       rinfo == R_PPC_GLOB_DAT ||
563       rinfo == R_PPC_ADDR32 ||
564       rinfo == R_PPC_RELATIVE)
565     {
566       *reloc_addr = finaladdr;
567     }
568 #ifndef RTLD_BOOTSTRAP
569   else if (rinfo == R_PPC_ADDR16_LO)
570     {
571       *(Elf32_Half*) reloc_addr = finaladdr;
572     }
573   else if (rinfo == R_PPC_ADDR16_HI)
574     {
575       *(Elf32_Half*) reloc_addr = finaladdr >> 16;
576     }
577   else if (rinfo == R_PPC_ADDR16_HA)
578     {
579       *(Elf32_Half*) reloc_addr = (finaladdr + 0x8000) >> 16;
580     }
581   else if (rinfo == R_PPC_REL24)
582     {
583       Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
584       if (delta << 6 >> 6 != delta)
585         {
586           _dl_signal_error(0, map->l_name,
587                            "R_PPC_REL24 relocation out of range");
588         }
589       *reloc_addr = *reloc_addr & 0xfc000003 | delta & 0x3fffffc;
590     }
591   else if (rinfo == R_PPC_ADDR24)
592     {
593       if (finaladdr << 6 >> 6 != finaladdr)
594         {
595           _dl_signal_error(0, map->l_name,
596                            "R_PPC_ADDR24 relocation out of range");
597         }
598       *reloc_addr = *reloc_addr & 0xfc000003 | finaladdr & 0x3fffffc;
599     }
600   else if (rinfo == R_PPC_COPY)
601     {
602       if (sym == NULL)
603         /* This can happen in trace mode when an object could not be
604            found.  */
605         return;
606       if (sym->st_size > refsym->st_size
607           || (_dl_verbose && sym->st_size < refsym->st_size))
608         {
609           const char *strtab;
610
611           strtab = ((void *) map->l_addr
612                     + map->l_info[DT_STRTAB]->d_un.d_ptr);
613           _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
614                             ": Symbol `", strtab + refsym->st_name,
615                             "' has different size in shared object, "
616                             "consider re-linking\n", NULL);
617         }
618       memcpy (reloc_addr, (char *) finaladdr, MIN (sym->st_size,
619                                                    refsym->st_size));
620     }
621 #endif
622   else if (rinfo == R_PPC_REL32)
623     {
624       *reloc_addr = finaladdr - (Elf32_Word) (char *) reloc_addr;
625     }
626   else if (rinfo == R_PPC_JMP_SLOT)
627     {
628       Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
629       if (delta << 6 >> 6 == delta)
630         *reloc_addr = OPCODE_B (delta);
631       else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000)
632         *reloc_addr = OPCODE_BA (finaladdr);
633       else
634         {
635           Elf32_Word *plt;
636           Elf32_Word index;
637
638           plt = (Elf32_Word *)((char *)map->l_addr
639                                + map->l_info[DT_PLTGOT]->d_un.d_val);
640           index = (reloc_addr - plt - PLT_INITIAL_ENTRY_WORDS)/2;
641           if (index >= PLT_DOUBLE_SIZE)
642             {
643               /* Slots greater than or equal to 2^13 have 4 words available
644                  instead of two.  */
645               /* FIXME: There are some possible race conditions in this code,
646                  when called from 'fixup'.
647
648                  1) Suppose that a lazy PLT entry is executing, a
649                  context switch between threads (or a signal) occurs,
650                  and the new thread or signal handler calls the same
651                  lazy PLT entry.  Then the PLT entry would be changed
652                  while it's being run, which will cause a segfault
653                  (almost always).
654
655                  2) Suppose the reverse: that a lazy PLT entry is
656                  being updated, a context switch occurs, and the new
657                  code calls the lazy PLT entry that is being updated.
658                  Then the half-fixed PLT entry will be executed, which
659                  will also almost always cause a segfault.
660
661                  These problems don't happen with the 2-word entries, because
662                  only one of the two instructions are changed when a lazy
663                  entry is retargeted at the actual PLT entry; the li
664                  instruction stays the same (we have to update it anyway,
665                  because we might not be updating a lazy PLT entry).  */
666               reloc_addr[0] = OPCODE_LI (11, finaladdr);
667               reloc_addr[1] = OPCODE_ADDIS (11, 11, finaladdr + 0x8000 >> 16);
668               reloc_addr[2] = OPCODE_MTCTR (11);
669               reloc_addr[3] = OPCODE_BCTR ();
670             }
671           else
672             {
673               Elf32_Word num_plt_entries;
674
675               num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
676                                  / sizeof(Elf32_Rela));
677
678               plt[index+PLT_DATA_START_WORDS (num_plt_entries)] = finaladdr;
679               reloc_addr[0] = OPCODE_LI (11, index*4);
680               reloc_addr[1] =
681                 OPCODE_B (-(4*(index*2
682                                + 1
683                                - PLT_LONGBRANCH_ENTRY_WORDS
684                                + PLT_INITIAL_ENTRY_WORDS)));
685             }
686         }
687       MODIFIED_CODE (reloc_addr);
688     }
689   else
690     {
691 #ifdef RTLD_BOOTSTRAP
692       PPC_DIE;  /* There is no point calling _dl_sysdep_error, it
693                    almost certainly hasn't been relocated properly.  */
694 #else
695       _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
696                         ": Unknown relocation type\n", NULL);
697 #endif
698     }
699
700 #ifndef RTLD_BOOTSTRAP
701   if (rinfo == R_PPC_ADDR16_LO ||
702       rinfo == R_PPC_ADDR16_HI ||
703       rinfo == R_PPC_ADDR16_HA ||
704       rinfo == R_PPC_REL24 ||
705       rinfo == R_PPC_ADDR24)
706     MODIFIED_CODE_NOQUEUE (reloc_addr);
707 #endif
708 }
709
710 #define ELF_MACHINE_NO_REL 1
711
712 #endif