mdreloc.c revision 1.6 1 /* $NetBSD: mdreloc.c,v 1.6 2022/12/03 09:39:44 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: mdreloc.c,v 1.6 2022/12/03 09:39:44 skrll Exp $");
35 #endif /* not lint */
36
37 #include <sys/types.h>
38 #include <sys/endian.h>
39 #include <sys/tls.h>
40
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "debug.h"
45 #include "rtld.h"
46
47 void _rtld_bind_start(void);
48 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
49 void *_rtld_bind(const Obj_Entry *, Elf_Word);
50
51 void
52 _rtld_setup_pltgot(const Obj_Entry *obj)
53 {
54 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
55 obj->pltgot[1] = (Elf_Addr) obj;
56 }
57
58 void
59 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
60 {
61 const Elf_Rela *rela = NULL, *relalim;
62 Elf_Addr relasz = 0;
63
64 for (; dynp->d_tag != DT_NULL; dynp++) {
65 switch (dynp->d_tag) {
66 case DT_RELA:
67 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr);
68 break;
69 case DT_RELASZ:
70 relasz = dynp->d_un.d_val;
71 break;
72 }
73 }
74
75 relalim = (const Elf_Rela *)((uintptr_t)rela + relasz);
76 for (; rela < relalim; rela++) {
77 Elf_Word r_type = ELF_R_TYPE(rela->r_info);
78 Elf_Addr *where = (Elf_Addr *)(relocbase + rela->r_offset);
79
80 switch (r_type) {
81 case R_TYPE(RELATIVE): {
82 Elf_Addr val = relocbase + rela->r_addend;
83 *where = val;
84 rdbg(("RELATIVE/L(%p) -> %p in <self>",
85 where, (void *)val));
86 break;
87 }
88
89 case R_TYPE(NONE):
90 break;
91
92 default:
93 abort();
94 }
95 }
96 }
97
98 int
99 _rtld_relocate_nonplt_objects(Obj_Entry *obj)
100 {
101 const Elf_Rela *rela;
102 const Elf_Sym *def = NULL;
103 const Obj_Entry *defobj = NULL;
104 unsigned long last_symnum = ULONG_MAX;
105
106 for (rela = obj->rela; rela < obj->relalim; rela++) {
107 Elf_Addr * const where =
108 (Elf_Addr *)(obj->relocbase + rela->r_offset);
109 const Elf_Word r_type = ELF_R_TYPE(rela->r_info);
110 unsigned long symnum;
111
112 switch (r_type) {
113 case R_TYPESZ(ADDR):
114 case R_TYPESZ(TLS_DTPMOD):
115 case R_TYPESZ(TLS_DTPREL):
116 symnum = ELF_R_SYM(rela->r_info);
117 if (last_symnum != symnum) {
118 last_symnum = symnum;
119 def = _rtld_find_symdef(symnum, obj, &defobj,
120 false);
121 if (def == NULL)
122 return -1;
123 }
124 break;
125 default:
126 break;
127 }
128
129 switch (r_type) {
130 case R_TYPE(NONE):
131 break;
132
133 case R_TYPE(RELATIVE): {
134 symnum = ELF_R_SYM(rela->r_info);
135 def = obj->symtab + symnum;
136
137 Elf_Addr val = (Elf_Addr)obj->relocbase + rela->r_addend;
138
139 rdbg(("RELATIVE(%p) -> %p (%s) in %s",
140 where, (void *)val,
141 obj->strtab + def->st_name, obj->path));
142
143 *where = val;
144 break;
145 }
146
147 case R_TYPESZ(ADDR): {
148 Elf_Addr val = (Elf_Addr)defobj->relocbase + rela->r_addend;
149
150 *where = val;
151 rdbg(("ADDR %s in %s --> %p in %s",
152 obj->strtab + obj->symtab[r_symndx].st_name,
153 obj->path, (void *)val, defobj->path));
154 break;
155 }
156
157 case R_TYPESZ(TLS_DTPMOD): {
158 Elf_Addr val = (Elf_Addr)defobj->tlsindex + rela->r_addend;
159
160 *where = val;
161 rdbg(("DTPMOD %s in %s --> %p in %s",
162 obj->strtab + obj->symtab[r_symndx].st_name,
163 obj->path, (void *)val, defobj->path));
164 break;
165 }
166
167 case R_TYPESZ(TLS_DTPREL): {
168 Elf_Addr old = *where;
169 Elf_Addr val = old;
170
171 if (!defobj->tls_done && _rtld_tls_offset_allocate(obj))
172 return -1;
173
174 val = (Elf_Addr)def->st_value - TLS_DTV_OFFSET;
175 *where = val;
176
177 rdbg(("DTPREL %s in %s --> %p in %s",
178 obj->strtab + obj->symtab[r_symndx].st_name,
179 obj->path, (void *)val, defobj->path));
180 break;
181 }
182
183 default:
184 rdbg(("sym = %lu, type = %lu, offset = %p, "
185 "addend = %p, contents = %p, symbol = %s",
186 (u_long)ELF_R_SYM(rela->r_info),
187 (u_long)ELF_R_TYPE(rela->r_info),
188 (void *)rela->r_offset, (void *)rela->r_addend,
189 (void *)load_ptr(where, sizeof(Elf_Addr)),
190 obj->strtab + obj->symtab[r_symndx].st_name));
191 _rtld_error("%s: Unsupported relocation type %ld "
192 "in non-PLT relocations",
193 obj->path, (u_long)r_type);
194 return -1;
195 }
196 }
197
198 return 0;
199 }
200
201 int
202 _rtld_relocate_plt_lazy(Obj_Entry *obj)
203 {
204 /* PLT fixups were done above in the GOT relocation. */
205 return 0;
206 }
207
208 static int
209 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel,
210 Elf_Addr *tp)
211 {
212 const Obj_Entry *defobj;
213 Elf_Addr new_value;
214
215 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JMP_SLOT));
216
217 const Elf_Sym *def = _rtld_find_plt_symdef(ELF_R_SYM(rel->r_info),
218 obj, &defobj, tp != NULL);
219 if (__predict_false(def == NULL))
220 return -1;
221 if (__predict_false(def == &_rtld_sym_zero))
222 return -1;
223
224 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
225 if (tp == NULL)
226 return 0;
227 new_value = _rtld_resolve_ifunc(defobj, def);
228 } else {
229 new_value = (Elf_Addr)(defobj->relocbase + def->st_value);
230 }
231 rdbg(("bind now/fixup in %s --> new=%p",
232 defobj->strtab + def->st_name, (void *)new_value));
233 *(Elf_Addr *)(obj->relocbase + rel->r_offset) = new_value;
234
235 if (tp)
236 *tp = new_value;
237 return 0;
238 }
239
240 void *
241 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
242 {
243 const Elf_Rel *pltrel = (const Elf_Rel *)(obj->pltrel + reloff);
244 Elf_Addr new_value;
245 int err;
246
247 _rtld_shared_enter();
248 err = _rtld_relocate_plt_object(obj, pltrel, &new_value);
249 if (err)
250 _rtld_die();
251 _rtld_shared_exit();
252
253 return (caddr_t)new_value;
254 }
255
256 int
257 _rtld_relocate_plt_objects(const Obj_Entry *obj)
258 {
259
260 for (const Elf_Rel *rel = obj->pltrel; rel < obj->pltrellim; rel++) {
261 if (_rtld_relocate_plt_object(obj, rel, NULL) < 0)
262 return -1;
263 }
264
265 return 0;
266 }
267