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