reloc.c revision 1.114 1 /* $NetBSD: reloc.c,v 1.114 2018/12/30 01:48:37 christos Exp $ */
2
3 /*
4 * Copyright 1996 John D. Polstra.
5 * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by John Polstra.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * Dynamic linker for ELF.
36 *
37 * John Polstra <jdp (at) polstra.com>.
38 */
39
40 #include <sys/cdefs.h>
41 #ifndef lint
42 __RCSID("$NetBSD: reloc.c,v 1.114 2018/12/30 01:48:37 christos Exp $");
43 #endif /* not lint */
44
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <sys/types.h>
54 #include <sys/mman.h>
55 #include <sys/bitops.h>
56 #include <dirent.h>
57
58 #include "debug.h"
59 #include "rtld.h"
60
61 #ifndef RTLD_INHIBIT_COPY_RELOCS
62 static int _rtld_do_copy_relocation(const Obj_Entry *, const Elf_Rela *);
63
64 static int
65 _rtld_do_copy_relocation(const Obj_Entry *dstobj, const Elf_Rela *rela)
66 {
67 void *dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
68 const Elf_Sym *dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
69 const char *name = dstobj->strtab + dstsym->st_name;
70 unsigned long hash = _rtld_elf_hash(name);
71 size_t size = dstsym->st_size;
72 const void *srcaddr;
73 const Elf_Sym *srcsym = NULL;
74 Obj_Entry *srcobj;
75
76 if (__predict_false(size == 0)) {
77 #if defined(__powerpc__) && !defined(__LP64) /* PR port-macppc/47464 */
78 if (strcmp(name, "_SDA_BASE_") == 0
79 || strcmp(name, "_SDA2_BASE_") == 0)
80 {
81 rdbg(("COPY %s %s --> ignoring old binutils bug",
82 dstobj->path, name));
83 return 0;
84 }
85 #endif
86 #if 0 /* shall we warn? */
87 xwarnx("%s: zero size COPY relocation for \"%s\"",
88 dstobj->path, name);
89 #endif
90 }
91
92 for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
93 srcsym = _rtld_symlook_obj(name, hash, srcobj, 0,
94 _rtld_fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)));
95 if (srcsym != NULL)
96 break;
97 }
98
99 if (srcobj == NULL) {
100 _rtld_error("Undefined symbol \"%s\" referenced from COPY"
101 " relocation in %s", name, dstobj->path);
102 return (-1);
103 }
104 srcaddr = (const void *)(srcobj->relocbase + srcsym->st_value);
105 rdbg(("COPY %s %s %s --> src=%p dst=%p size %ld",
106 dstobj->path, srcobj->path, name, srcaddr,
107 (void *)dstaddr, (long)size));
108 (void)memcpy(dstaddr, srcaddr, size);
109 return (0);
110 }
111 #endif /* RTLD_INHIBIT_COPY_RELOCS */
112
113
114 /*
115 * Process the special R_xxx_COPY relocations in the main program. These
116 * copy data from a shared object into a region in the main program's BSS
117 * segment.
118 *
119 * Returns 0 on success, -1 on failure.
120 */
121 int
122 _rtld_do_copy_relocations(const Obj_Entry *dstobj)
123 {
124 #ifndef RTLD_INHIBIT_COPY_RELOCS
125
126 /* COPY relocations are invalid elsewhere */
127 assert(!dstobj->isdynamic);
128
129 if (dstobj->rel != NULL) {
130 const Elf_Rel *rel;
131 for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) {
132 if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) {
133 Elf_Rela ourrela;
134 ourrela.r_info = rel->r_info;
135 ourrela.r_offset = rel->r_offset;
136 ourrela.r_addend = 0;
137 if (_rtld_do_copy_relocation(dstobj,
138 &ourrela) < 0)
139 return (-1);
140 }
141 }
142 }
143 if (dstobj->rela != NULL) {
144 const Elf_Rela *rela;
145 for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) {
146 if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) {
147 if (_rtld_do_copy_relocation(dstobj, rela) < 0)
148 return (-1);
149 }
150 }
151 }
152 #ifdef GNU_RELRO
153 if (_rtld_relro(dstobj, true) == -1)
154 return -1;
155 #endif
156 #endif /* RTLD_INHIBIT_COPY_RELOCS */
157
158 return (0);
159 }
160
161 /*
162 * Relocate newly-loaded shared objects. The argument is a pointer to
163 * the Obj_Entry for the first such object. All objects from the first
164 * to the end of the list of objects are relocated. Returns 0 on success,
165 * or -1 on failure.
166 */
167 int
168 _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
169 {
170 Obj_Entry *obj;
171 int ok = 1;
172
173 for (obj = first; obj != NULL; obj = obj->next) {
174 if (obj->nbuckets == 0 || obj->nchains == 0 ||
175 obj->buckets == NULL || obj->symtab == NULL ||
176 obj->strtab == NULL) {
177 _rtld_error("%s: Shared object has no run-time"
178 " symbol table", obj->path);
179 return -1;
180 }
181 if (obj->nbuckets == UINT32_MAX) {
182 _rtld_error("%s: Symbol table too large", obj->path);
183 return -1;
184 }
185 rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
186 obj->path,
187 (long)(obj->rellim - obj->rel),
188 (long)(obj->relalim - obj->rela),
189 (long)(obj->pltrellim - obj->pltrel),
190 (long)(obj->pltrelalim - obj->pltrela)));
191
192 if (obj->textrel) {
193 xwarnx("%s: text relocations", obj->path);
194 /*
195 * There are relocations to the write-protected text
196 * segment.
197 */
198 if (mprotect(obj->mapbase, obj->textsize,
199 PROT_READ | PROT_WRITE) == -1) {
200 _rtld_error("%s: Cannot write-enable text "
201 "segment: %s", obj->path, xstrerror(errno));
202 return -1;
203 }
204 }
205 dbg(("doing non-PLT relocations"));
206 if (_rtld_relocate_nonplt_objects(obj) < 0)
207 ok = 0;
208 if (obj->textrel) { /* Re-protected the text segment. */
209 if (mprotect(obj->mapbase, obj->textsize,
210 PROT_READ | PROT_EXEC) == -1) {
211 _rtld_error("%s: Cannot write-protect text "
212 "segment: %s", obj->path, xstrerror(errno));
213 return -1;
214 }
215 }
216 dbg(("doing lazy PLT binding"));
217 if (_rtld_relocate_plt_lazy(obj) < 0)
218 ok = 0;
219 if (obj->z_now || bind_now) {
220 dbg(("doing immediate PLT binding"));
221 if (_rtld_relocate_plt_objects(obj) < 0)
222 ok = 0;
223 }
224 if (!ok)
225 return -1;
226
227 dbg(("fixing up PLTGOT"));
228 /* Set the special PLTGOT entries. */
229 if (obj->pltgot != NULL)
230 _rtld_setup_pltgot(obj);
231 #ifdef GNU_RELRO
232 if (_rtld_relro(obj, false) == -1)
233 return -1;
234 #endif
235 }
236 return 0;
237 }
238
239 Elf_Addr
240 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
241 {
242 Elf_Addr target;
243
244 _rtld_shared_exit();
245 target = _rtld_resolve_ifunc2(obj,
246 (Elf_Addr)obj->relocbase + def->st_value);
247 _rtld_shared_enter();
248 return target;
249 }
250
251 Elf_Addr
252 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
253 {
254 Elf_Addr target;
255
256 target = _rtld_call_function_addr(obj, addr);
257
258 return target;
259 }
260
261 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
262 # ifdef __sparc__
263 # include <machine/elf_support.h>
264 # endif
265
266 void
267 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
268 {
269 const Elf_Rela *rela;
270 Elf_Addr *where;
271 #ifdef __sparc__
272 Elf_Word *where2;
273 #endif
274 Elf_Addr target;
275
276 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
277 rela = obj->pltrelalim - obj->ifunc_remaining--;
278 #ifdef __sparc__
279 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
280 #else
281 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
282 #endif
283 if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
284 continue;
285 #ifdef __sparc__
286 where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
287 #else
288 where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
289 #endif
290 target = (Elf_Addr)(obj->relocbase + rela->r_addend);
291 _rtld_exclusive_exit(mask);
292 target = _rtld_resolve_ifunc2(obj, target);
293 _rtld_exclusive_enter(mask);
294 #ifdef __sparc__
295 sparc_write_branch(where2 + 1, (void *)target);
296 #else
297 if (*where != target)
298 *where = target;
299 #endif
300 }
301
302 while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
303 rela = obj->relalim - obj->ifunc_remaining_nonplt--;
304 if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
305 continue;
306 where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
307 target = (Elf_Addr)(obj->relocbase + rela->r_addend);
308 _rtld_exclusive_exit(mask);
309 target = _rtld_resolve_ifunc2(obj, target);
310 _rtld_exclusive_enter(mask);
311 if (*where != target)
312 *where = target;
313 }
314 }
315 #endif
316
317 #ifdef RTLD_COMMON_CALL_IFUNC_REL
318 void
319 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
320 {
321 const Elf_Rel *rel;
322 Elf_Addr *where, target;
323
324 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
325 rel = obj->pltrellim - obj->ifunc_remaining;
326 --obj->ifunc_remaining;
327 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
328 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
329 _rtld_exclusive_exit(mask);
330 target = _rtld_resolve_ifunc2(obj, *where);
331 _rtld_exclusive_enter(mask);
332 if (*where != target)
333 *where = target;
334 }
335 }
336
337 while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
338 rel = obj->rellim - obj->ifunc_remaining_nonplt--;
339 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
340 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
341 _rtld_exclusive_exit(mask);
342 target = _rtld_resolve_ifunc2(obj, *where);
343 _rtld_exclusive_enter(mask);
344 if (*where != target)
345 *where = target;
346 }
347 }
348 }
349 #endif
350