Merge from vendor branch NCURSES:
[dragonfly.git] / libexec / rtld-elf / i386 / 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/i386/reloc.c,v 1.6.2.2 2002/06/16 20:02:09 dillon Exp $
26  * $DragonFly: src/libexec/rtld-elf/i386/reloc.c,v 1.3 2005/02/04 00:24:23 joerg 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 /*
52  * Process the special R_386_COPY relocations in the main program.  These
53  * copy data from a shared object into a region in the main program's BSS
54  * segment.
55  *
56  * Returns 0 on success, -1 on failure.
57  */
58 int
59 do_copy_relocations(Obj_Entry *dstobj)
60 {
61     const Elf_Rel *rellim;
62     const Elf_Rel *rel;
63
64     assert(dstobj->mainprog);   /* COPY relocations are invalid elsewhere */
65
66     rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
67     for (rel = dstobj->rel;  rel < rellim;  rel++) {
68         if (ELF_R_TYPE(rel->r_info) == R_386_COPY) {
69             void *dstaddr;
70             const Elf_Sym *dstsym;
71             const char *name;
72             unsigned long hash;
73             size_t size;
74             const void *srcaddr;
75             const Elf_Sym *srcsym;
76             Obj_Entry *srcobj;
77
78             dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
79             dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
80             name = dstobj->strtab + dstsym->st_name;
81             hash = elf_hash(name);
82             size = dstsym->st_size;
83
84             for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next)
85                 if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL)
86                     break;
87
88             if (srcobj == NULL) {
89                 _rtld_error("Undefined symbol \"%s\" referenced from COPY"
90                   " relocation in %s", name, dstobj->path);
91                 return -1;
92             }
93
94             srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
95             memcpy(dstaddr, srcaddr, size);
96         }
97     }
98
99     return 0;
100 }
101
102 /* Initialize the special GOT entries. */
103 void
104 init_pltgot(Obj_Entry *obj)
105 {
106     if (obj->pltgot != NULL) {
107         obj->pltgot[1] = (Elf_Addr) obj;
108         obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
109     }
110 }
111
112 /* Process the non-PLT relocations. */
113 int
114 reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
115 {
116         const Elf_Rel *rellim;
117         const Elf_Rel *rel;
118         SymCache *cache;
119         int bytes = obj->nchains * sizeof(SymCache);
120         int r = -1;
121
122         /*
123          * The dynamic loader may be called from a thread, we have
124          * limited amounts of stack available so we cannot use alloca().
125          */
126         cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
127         if (cache == MAP_FAILED)
128             cache = NULL;
129
130         rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize);
131         for (rel = obj->rel;  rel < rellim;  rel++) {
132             Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rel->r_offset);
133
134             switch (ELF_R_TYPE(rel->r_info)) {
135
136             case R_386_NONE:
137                 break;
138
139             case R_386_32:
140                 {
141                     const Elf_Sym *def;
142                     const Obj_Entry *defobj;
143
144                     def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
145                       false, cache);
146                     if (def == NULL)
147                         goto done;
148
149                     *where += (Elf_Addr) (defobj->relocbase + def->st_value);
150                 }
151                 break;
152
153             case R_386_PC32:
154                 /*
155                  * I don't think the dynamic linker should ever see this
156                  * type of relocation.  But the binutils-2.6 tools sometimes
157                  * generate it.
158                  */
159                 {
160                     const Elf_Sym *def;
161                     const Obj_Entry *defobj;
162
163                     def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
164                       false, cache);
165                     if (def == NULL)
166                         goto done;
167
168                     *where +=
169                       (Elf_Addr) (defobj->relocbase + def->st_value) -
170                       (Elf_Addr) where;
171                 }
172                 break;
173
174             case R_386_COPY:
175                 /*
176                  * These are deferred until all other relocations have
177                  * been done.  All we do here is make sure that the COPY
178                  * relocation is not in a shared library.  They are allowed
179                  * only in executable files.
180                  */
181                 if (!obj->mainprog) {
182                     _rtld_error("%s: Unexpected R_386_COPY relocation"
183                       " in shared library", obj->path);
184                     goto done;
185                 }
186                 break;
187
188             case R_386_GLOB_DAT:
189                 {
190                     const Elf_Sym *def;
191                     const Obj_Entry *defobj;
192
193                     def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
194                       false, cache);
195                     if (def == NULL)
196                         goto done;
197
198                     *where = (Elf_Addr) (defobj->relocbase + def->st_value);
199                 }
200                 break;
201
202             case R_386_RELATIVE:
203                 *where += (Elf_Addr) obj->relocbase;
204                 break;
205
206             default:
207                 _rtld_error("%s: Unsupported relocation type %d"
208                   " in non-PLT relocations\n", obj->path,
209                   ELF_R_TYPE(rel->r_info));
210                 goto done;
211             }
212         }
213         r = 0;
214 done:
215         if (cache)
216             munmap(cache, bytes);
217         return(r);
218 }
219
220 /* Process the PLT relocations. */
221 int
222 reloc_plt(Obj_Entry *obj)
223 {
224     const Elf_Rel *rellim;
225     const Elf_Rel *rel;
226
227     rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
228     for (rel = obj->pltrel;  rel < rellim;  rel++) {
229         Elf_Addr *where;
230
231         assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
232
233         /* Relocate the GOT slot pointing into the PLT. */
234         where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
235         *where += (Elf_Addr)obj->relocbase;
236     }
237     return 0;
238 }
239
240 /* Relocate the jump slots in an object. */
241 int
242 reloc_jmpslots(Obj_Entry *obj)
243 {
244     const Elf_Rel *rellim;
245     const Elf_Rel *rel;
246
247     if (obj->jmpslots_done)
248         return 0;
249     rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
250     for (rel = obj->pltrel;  rel < rellim;  rel++) {
251         Elf_Addr *where;
252         const Elf_Sym *def;
253         const Obj_Entry *defobj;
254
255         assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
256         where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
257         def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL);
258         if (def == NULL)
259             return -1;
260         reloc_jmpslot(where, (Elf_Addr)(defobj->relocbase + def->st_value));
261     }
262     obj->jmpslots_done = true;
263     return 0;
264 }