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