Initial import from FreeBSD RELENG_4:
[dragonfly.git] / libexec / rtld-elf / alpha / reloc.c
1 /*-
2  * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD: src/libexec/rtld-elf/alpha/reloc.c,v 1.10.2.5 2002/09/02 02:10:20 obrien Exp $
26  */
27
28 /*
29  * Dynamic linker for ELF.
30  *
31  * John Polstra <jdp@polstra.com>.
32  */
33
34 #include <sys/param.h>
35 #include <sys/mman.h>
36
37 #include <dlfcn.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "debug.h"
48 #include "rtld.h"
49
50 extern Elf_Dyn _GOT_END_;
51
52 /*
53  * Macros for loading/storing unaligned 64-bit values.  These are
54  * needed because relocations can point to unaligned data.  This
55  * occurs in the DWARF2 exception frame tables generated by the
56  * compiler, for instance.
57  *
58  * We don't use these when relocating jump slots and GOT entries,
59  * since they are guaranteed to be aligned.
60  */
61 #define load64(p) ({                                            \
62         Elf_Addr __res;                                         \
63         __asm__("ldq_u %0,%1" : "=r"(__res) : "m"(*(p)));       \
64         __res; })
65
66 #define store64(p, v)                                           \
67         __asm__("stq_u %1,%0" : "=m"(*(p)) : "r"(v))
68
69 /* Relocate a non-PLT object with addend. */
70 static int
71 reloc_non_plt_obj(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela,
72         SymCache *cache)
73 {
74         Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset);
75
76         switch (ELF_R_TYPE(rela->r_info)) {
77
78                 case R_ALPHA_NONE:
79                         break;
80
81                 case R_ALPHA_REFQUAD: {
82                         const Elf_Sym *def;
83                         const Obj_Entry *defobj;
84
85                         def = find_symdef(ELF_R_SYM(rela->r_info), obj,
86                             &defobj, false, cache);
87                         if (def == NULL)
88                                 return -1;
89                         store64(where,
90                             (Elf_Addr) (defobj->relocbase + def->st_value) +
91                             load64(where) + rela->r_addend);
92                 }
93                 break;
94
95                 case R_ALPHA_GLOB_DAT: {
96                         const Elf_Sym *def;
97                         const Obj_Entry *defobj;
98                         Elf_Addr val;
99
100                         def = find_symdef(ELF_R_SYM(rela->r_info), obj,
101                             &defobj, false, cache);
102                         if (def == NULL)
103                                 return -1;
104                         val = (Elf_Addr) (defobj->relocbase + def->st_value +
105                             rela->r_addend);
106                         if (load64(where) != val)
107                                 store64(where, val);
108                 }
109                 break;
110
111                 case R_ALPHA_RELATIVE: {
112                         if (obj != obj_rtld ||
113                             (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ ||
114                             (caddr_t)where >= (caddr_t)&_GOT_END_)
115                                 store64(where,
116                                     load64(where) + (Elf_Addr) obj->relocbase);
117                 }
118                 break;
119
120                 case R_ALPHA_COPY: {
121                         /*
122                          * These are deferred until all other relocations
123                          * have been done.  All we do here is make sure
124                          * that the COPY relocation is not in a shared
125                          * library.  They are allowed only in executable
126                          * files.
127                         */
128                         if (!obj->mainprog) {
129                                 _rtld_error("%s: Unexpected R_COPY "
130                                     " relocation in shared library",
131                                     obj->path);
132                                 return -1;
133                         }
134                 }
135                 break;
136
137                 default:
138                         _rtld_error("%s: Unsupported relocation type %d"
139                             " in non-PLT relocations\n", obj->path,
140                             ELF_R_TYPE(rela->r_info));
141                         return -1;
142         }
143         return(0);
144 }
145
146 /* Process the non-PLT relocations. */
147 int
148 reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
149 {
150         const Elf_Rel *rellim;
151         const Elf_Rel *rel;
152         const Elf_Rela *relalim;
153         const Elf_Rela *rela;
154         SymCache *cache;
155         int bytes = obj->nchains * sizeof(SymCache);
156         int r = -1;
157
158         /*
159          * The dynamic loader may be called from a thread, we have
160          * limited amounts of stack available so we cannot use alloca().
161          */
162         cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
163         if (cache == MAP_FAILED)
164             cache = NULL;
165         if (cache != NULL)
166             memset(cache, 0, bytes);
167
168         /* Perform relocations without addend if there are any: */
169         rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize);
170         for (rel = obj->rel;  obj->rel != NULL && rel < rellim;  rel++) {
171                 Elf_Rela locrela;
172
173                 locrela.r_info = rel->r_info;
174                 locrela.r_offset = rel->r_offset;
175                 locrela.r_addend = 0;
176                 if (reloc_non_plt_obj(obj_rtld, obj, &locrela, cache))
177                         goto done;
178         }
179
180         /* Perform relocations with addend if there are any: */
181         relalim = (const Elf_Rela *) ((caddr_t) obj->rela + obj->relasize);
182         for (rela = obj->rela;  obj->rela != NULL && rela < relalim;  rela++) {
183                 if (reloc_non_plt_obj(obj_rtld, obj, rela, cache))
184                         goto done;
185         }
186         r = 0;
187 done:
188         if (cache)
189             munmap(cache, bytes);
190         return(r);
191 }
192
193 /* Process the PLT relocations. */
194 int
195 reloc_plt(Obj_Entry *obj)
196 {
197     /* All PLT relocations are the same kind: either Elf_Rel or Elf_Rela. */
198     if (obj->pltrelsize != 0) {
199         const Elf_Rel *rellim;
200         const Elf_Rel *rel;
201
202         rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
203         for (rel = obj->pltrel;  rel < rellim;  rel++) {
204             Elf_Addr *where;
205
206             assert(ELF_R_TYPE(rel->r_info) == R_ALPHA_JMP_SLOT);
207
208             /* Relocate the GOT slot pointing into the PLT. */
209             where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
210             *where += (Elf_Addr)obj->relocbase;
211         }
212     } else {
213         const Elf_Rela *relalim;
214         const Elf_Rela *rela;
215
216         relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
217         for (rela = obj->pltrela;  rela < relalim;  rela++) {
218             Elf_Addr *where;
219
220             assert(ELF_R_TYPE(rela->r_info) == R_ALPHA_JMP_SLOT);
221
222             /* Relocate the GOT slot pointing into the PLT. */
223             where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
224             *where += (Elf_Addr)obj->relocbase;
225         }
226     }
227     return 0;
228 }
229
230 /* Relocate the jump slots in an object. */
231 int
232 reloc_jmpslots(Obj_Entry *obj)
233 {
234     if (obj->jmpslots_done)
235         return 0;
236     /* All PLT relocations are the same kind: either Elf_Rel or Elf_Rela. */
237     if (obj->pltrelsize != 0) {
238         const Elf_Rel *rellim;
239         const Elf_Rel *rel;
240
241         rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
242         for (rel = obj->pltrel;  rel < rellim;  rel++) {
243             Elf_Addr *where;
244             const Elf_Sym *def;
245             const Obj_Entry *defobj;
246
247             assert(ELF_R_TYPE(rel->r_info) == R_ALPHA_JMP_SLOT);
248             where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
249             def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true,
250                 NULL);
251             if (def == NULL)
252                 return -1;
253             reloc_jmpslot(where,
254               (Elf_Addr)(defobj->relocbase + def->st_value));
255         }
256     } else {
257         const Elf_Rela *relalim;
258         const Elf_Rela *rela;
259
260         relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
261         for (rela = obj->pltrela;  rela < relalim;  rela++) {
262             Elf_Addr *where;
263             const Elf_Sym *def;
264             const Obj_Entry *defobj;
265
266             assert(ELF_R_TYPE(rela->r_info) == R_ALPHA_JMP_SLOT);
267             where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
268             def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true,
269                 NULL);
270             if (def == NULL)
271                 return -1;
272             reloc_jmpslot(where,
273               (Elf_Addr)(defobj->relocbase + def->st_value));
274         }
275     }
276     obj->jmpslots_done = true;
277     return 0;
278 }
279
280 /* Fixup the jump slot at "where" to transfer control to "target". */
281 void
282 reloc_jmpslot(Elf_Addr *where, Elf_Addr target)
283 {
284     Elf_Addr stubaddr;
285
286     dbg(" reloc_jmpslot: where=%p, target=%p", (void *)where, (void *)target);
287     stubaddr = *where;
288     if (stubaddr != target) {
289         int64_t delta;
290         u_int32_t inst[3];
291         int instct;
292         Elf_Addr pc;
293         int64_t idisp;
294         u_int32_t *stubptr;
295
296         /* Point this GOT entry directly at the target. */
297         *where = target;
298
299         /*
300          * There may be multiple GOT tables, each with an entry
301          * pointing to the stub in the PLT.  But we can only find and
302          * fix up the first GOT entry.  So we must rewrite the stub as
303          * well, to perform a call to the target if it is executed.
304          *
305          * When the stub gets control, register pv ($27) contains its
306          * address.  We adjust its value so that it points to the
307          * target, and then jump indirect through it.
308          *
309          * Each PLT entry has room for 3 instructions.  If the
310          * adjustment amount fits in a signed 32-bit integer, we can
311          * simply add it to register pv.  Otherwise we must load the
312          * GOT entry itself into the pv register.
313          */
314         delta = target - stubaddr;
315         dbg("  stubaddr=%p, where-stubaddr=%ld, delta=%ld", (void *)stubaddr,
316           (long)where - (long)stubaddr, (long)delta);
317         instct = 0;
318         if ((int32_t)delta == delta) {
319             /*
320              * We can adjust pv with a LDA, LDAH sequence.
321              *
322              * First build an LDA instruction to adjust the low 16 bits.
323              */
324             inst[instct++] = 0x08 << 26 | 27 << 21 | 27 << 16 |
325               (delta & 0xffff);
326             dbg("  LDA  $27,%d($27)", (int16_t)delta);
327             /*
328              * Adjust the delta to account for the effects of the LDA,
329              * including sign-extension.
330              */
331             delta -= (int16_t)delta;
332             if (delta != 0) {
333                 /* Build an LDAH instruction to adjust the high 16 bits. */
334                 inst[instct++] = 0x09 << 26 | 27 << 21 | 27 << 16 |
335                   (delta >> 16 & 0xffff);
336                 dbg("  LDAH $27,%d($27)", (int16_t)(delta >> 16));
337             }
338         } else {
339             int64_t dhigh;
340
341             /* We must load the GOT entry from memory. */
342             delta = (Elf_Addr)where - stubaddr;
343             /*
344              * If the GOT entry is too far away from the PLT entry,
345              * then punt. This PLT entry will have to be looked up
346              * manually for all GOT entries except the first one.
347              * The program will still run, albeit very slowly.  It's
348              * extremely unlikely that this case could ever arise in
349              * practice, but we might as well handle it correctly if
350              * it does.
351              */
352             if ((int32_t)delta != delta) {
353                 dbg("  PLT stub too far from GOT to relocate");
354                 return;
355             }
356             dhigh = delta - (int16_t)delta;
357             if (dhigh != 0) {
358                 /* Build an LDAH instruction to adjust the high 16 bits. */
359                 inst[instct++] = 0x09 << 26 | 27 << 21 | 27 << 16 |
360                   (dhigh >> 16 & 0xffff);
361                 dbg("  LDAH $27,%d($27)", (int16_t)(dhigh >> 16));
362             }
363             /* Build an LDQ to load the GOT entry. */
364             inst[instct++] = 0x29 << 26 | 27 << 21 | 27 << 16 |
365               (delta & 0xffff);
366             dbg("  LDQ  $27,%d($27)", (int16_t)delta);
367         }
368
369         /*
370          * Build a JMP or BR instruction to jump to the target.  If
371          * the instruction displacement fits in a sign-extended 21-bit
372          * field, we can use the more efficient BR instruction.
373          * Otherwise we have to jump indirect through the pv register.
374          */
375         pc = stubaddr + 4 * (instct + 1);
376         idisp = (int64_t)(target - pc) >> 2;
377         if (-0x100000 <= idisp && idisp < 0x100000) {
378             inst[instct++] = 0x30 << 26 | 31 << 21 | (idisp & 0x1fffff);
379             dbg("  BR   $31,%p", (void *)target);
380         } else {
381             inst[instct++] = 0x1a << 26 | 31 << 21 | 27 << 16 |
382               (idisp & 0x3fff);
383             dbg("  JMP  $31,($27),%d", (int)(idisp & 0x3fff));
384         }
385
386         /*
387          * Fill in the tail of the PLT entry first for reentrancy.
388          * Until we have overwritten the first instruction (an
389          * unconditional branch), the remaining instructions have no
390          * effect.
391          */
392         stubptr = (u_int32_t *)stubaddr;
393         while (instct > 1) {
394             instct--;
395             stubptr[instct] = inst[instct];
396         }
397         /*
398          * Commit the tail of the instruction sequence to memory
399          * before overwriting the first instruction.
400          */
401         __asm__ __volatile__("wmb" : : : "memory");
402         stubptr[0] = inst[0];
403     }
404 }
405
406 /* Process an R_ALPHA_COPY relocation. */
407 static int
408 do_copy_relocation(Obj_Entry *dstobj, const Elf_Rela *rela)
409 {
410         void *dstaddr;
411         const Elf_Sym *dstsym;
412         const char *name;
413         unsigned long hash;
414         size_t size;
415         const void *srcaddr;
416         const Elf_Sym *srcsym;
417         Obj_Entry *srcobj;
418
419         dstaddr = (void *) (dstobj->relocbase + rela->r_offset);
420         dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
421         name = dstobj->strtab + dstsym->st_name;
422         hash = elf_hash(name);
423         size = dstsym->st_size;
424
425         for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next)
426                 if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL)
427                         break;
428
429         if (srcobj == NULL) {
430                 _rtld_error("Undefined symbol \"%s\" referenced from COPY"
431                     " relocation in %s", name, dstobj->path);
432                 return -1;
433         }
434
435         srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
436         memcpy(dstaddr, srcaddr, size);
437         return 0;
438 }
439
440 /*
441  * Process the special R_ALPHA_COPY relocations in the main program.  These
442  * copy data from a shared object into a region in the main program's BSS
443  * segment.
444  *
445  * Returns 0 on success, -1 on failure.
446  */
447 int
448 do_copy_relocations(Obj_Entry *dstobj)
449 {
450         const Elf_Rel *rellim;
451         const Elf_Rel *rel;
452         const Elf_Rela *relalim;
453         const Elf_Rela *rela;
454
455         assert(dstobj->mainprog);       /* COPY relocations are invalid elsewhere */
456
457         rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
458         for (rel = dstobj->rel; dstobj->rel != NULL && rel < rellim;  rel++) {
459                 if (ELF_R_TYPE(rel->r_info) == R_ALPHA_COPY) {
460                         Elf_Rela locrela;
461
462                         locrela.r_info = rel->r_info;
463                         locrela.r_offset = rel->r_offset;
464                         locrela.r_addend = 0;
465                         if (do_copy_relocation(dstobj, &locrela))
466                                 return -1;
467                 }
468         }
469
470         relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela +
471             dstobj->relasize);
472         for (rela = dstobj->rela; dstobj->rela != NULL && rela < relalim;
473             rela++) {
474                 if (ELF_R_TYPE(rela->r_info) == R_ALPHA_COPY) {
475                         if (do_copy_relocation(dstobj, rela))
476                                 return -1;
477                 }
478         }
479
480         return 0;
481 }
482
483 /* Initialize the special PLT entries. */
484 void
485 init_pltgot(Obj_Entry *obj)
486 {
487         u_int32_t *pltgot;
488
489         if (obj->pltgot != NULL &&
490             (obj->pltrelsize != 0 || obj->pltrelasize != 0)) {
491                 /*
492                  * This function will be called to perform the relocation.
493                  * Look for the ldah instruction from the old PLT format since
494                  * that will tell us what format we are trying to relocate.
495                  */
496                 pltgot = (u_int32_t *) obj->pltgot;
497                 if ((pltgot[8] & 0xffff0000) == 0x279f0000)
498                         obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start_old;
499                 else
500                         obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
501                 /* Identify this shared object */
502                 obj->pltgot[3] = (Elf_Addr) obj;
503         }
504 }