1 /*        $NetBSD: mdreloc.c,v 1.37 2025/04/18 12:56:47 riastradh Exp $         */
2 
3 /*
4  * Copyright 1996 John D. Polstra.
5  * Copyright 1996 Matt Thomas <matt@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  * m68k ELF relocations.
36  *
37  * References:
38  *
39  *        [M68KSYSVABI] System V Application Binary Interface: Motorola
40  *        68000 Processor Family Supplement, 1990, AT&T.
41  *        https://people.debian.org/~glaubitz/m68k-sysv-abi.pdf
42  *        https://web.archive.org/web/20250317195959/https://people.debian.org/~glaubitz/m68k-sysv-abi.pdf
43  */
44 
45 #include <sys/cdefs.h>
46 #ifndef lint
47 __RCSID("$NetBSD: mdreloc.c,v 1.37 2025/04/18 12:56:47 riastradh Exp $");
48 #endif /* not lint */
49 
50 #include <sys/types.h>
51 
52 #include "debug.h"
53 #include "rtld.h"
54 
55 #include <machine/lwp_private.h>
56 
57 void _rtld_bind_start(void);
58 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
59 caddr_t _rtld_bind(const Obj_Entry *, Elf_Word);
60 static inline int _rtld_relocate_plt_object(const Obj_Entry *,
61     const Elf_Rela *, Elf_Addr *);
62 
63 
64 void
_rtld_setup_pltgot(const Obj_Entry * obj)65 _rtld_setup_pltgot(const Obj_Entry *obj)
66 {
67           obj->pltgot[1] = (Elf_Addr) obj;
68           obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
69 }
70 
71 void
_rtld_relocate_nonplt_self(Elf_Dyn * dynp,Elf_Addr relocbase)72 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
73 {
74           const Elf_Rela *rela = 0, *relalim;
75           Elf_Addr relasz = 0;
76           Elf_Addr *where;
77 
78           for (; dynp->d_tag != DT_NULL; dynp++) {
79                     switch (dynp->d_tag) {
80                     case DT_RELA:
81                               rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr);
82                               break;
83                     case DT_RELASZ:
84                               relasz = dynp->d_un.d_val;
85                               break;
86                     }
87           }
88           relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz);
89           for (; rela < relalim; rela++) {
90                     where = (Elf_Addr *)(relocbase + rela->r_offset);
91                     *where += (Elf_Addr)relocbase;
92           }
93 }
94 
95 int
_rtld_relocate_nonplt_objects(Obj_Entry * obj)96 _rtld_relocate_nonplt_objects(Obj_Entry *obj)
97 {
98           const Elf_Rela *rela;
99           const Elf_Sym *def = NULL;
100           const Obj_Entry *defobj = NULL;
101           unsigned long last_symnum = ULONG_MAX;
102 
103           for (rela = obj->rela; rela < obj->relalim; rela++) {
104                     Elf_Addr        *where;
105                     Elf_Addr         tmp;
106                     unsigned long        symnum;
107 
108                     where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
109 
110                     switch (ELF_R_TYPE(rela->r_info)) {
111                     case R_TYPE(PC32):
112                     case R_TYPE(GOT32):
113                     case R_TYPE(32):
114                     case R_TYPE(GLOB_DAT):
115                     case R_TYPE(TLS_DTPMOD32):
116                     case R_TYPE(TLS_DTPREL32):
117                     case R_TYPE(TLS_TPREL32):
118                               symnum = ELF_R_SYM(rela->r_info);
119                               if (last_symnum != symnum) {
120                                         last_symnum = symnum;
121                                         def = _rtld_find_symdef(symnum, obj, &defobj,
122                                             false);
123                                         if (def == NULL)
124                                                   return -1;
125                               }
126                               break;
127                     default:
128                               break;
129                     }
130 
131                     switch (ELF_R_TYPE(rela->r_info)) {
132                     case R_TYPE(NONE):
133                               break;
134 
135 #if 1 /* XXX should not occur */
136                     case R_TYPE(PC32):
137                               tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
138                                   rela->r_addend) - (Elf_Addr)where;
139                               if (*where != tmp)
140                                         *where = tmp;
141                               rdbg(("PC32 %s in %s --> %p in %s",
142                                   obj->strtab + obj->symtab[symnum].st_name,
143                                   obj->path, (void *)*where, defobj->path));
144                               break;
145 
146                     case R_TYPE(GOT32):
147 #endif
148                     case R_TYPE(32):
149                     case R_TYPE(GLOB_DAT):
150                               tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
151                                   rela->r_addend);
152                               if (*where != tmp)
153                                         *where = tmp;
154                               rdbg(("32/GLOB_DAT %s in %s --> %p in %s",
155                                   obj->strtab + obj->symtab[symnum].st_name,
156                                   obj->path, (void *)*where, defobj->path));
157                               break;
158 
159                     case R_TYPE(RELATIVE):
160                               *where += (Elf_Addr)obj->relocbase;
161                               rdbg(("RELATIVE in %s --> %p", obj->path,
162                                   (void *)*where));
163                               break;
164 
165                     case R_TYPE(COPY):
166                               /*
167                                * These are deferred until all other relocations have
168                                * been done.  All we do here is make sure that the
169                                * COPY relocation is not in a shared library.  They
170                                * are allowed only in executable files.
171                                */
172                               if (obj->isdynamic) {
173                                         _rtld_error(
174                               "%s: Unexpected R_COPY relocation in shared library",
175                                             obj->path);
176                                         return -1;
177                               }
178                               rdbg(("COPY (avoid in main)"));
179                               break;
180 
181                     case R_TYPE(TLS_DTPMOD32):
182                               *where = (Elf_Addr)defobj->tlsindex;
183                               rdbg(("DTPMOD32 %s in %s --> %p in %s",
184                                   obj->strtab + obj->symtab[symnum].st_name,
185                                   obj->path, (void *)*where, defobj->path));
186                               break;
187 
188                     case R_TYPE(TLS_DTPREL32):
189                               *where = (Elf_Addr)(def->st_value + rela->r_addend
190                                   - TLS_DTV_OFFSET);
191                               rdbg(("DTPREL32 %s in %s --> %p in %s",
192                                   obj->strtab + obj->symtab[symnum].st_name,
193                                   obj->path, (void *)*where, defobj->path));
194                               break;
195 
196                     case R_TYPE(TLS_TPREL32):
197                               if (!defobj->tls_static &&
198                                   _rtld_tls_offset_allocate(__UNCONST(defobj)))
199                                         return -1;
200 
201                               *where = (Elf_Addr)(def->st_value + rela->r_addend
202                                   + defobj->tlsoffset - TLS_TP_OFFSET);
203                               rdbg(("TPREL32 %s in %s --> %p in %s",
204                                   obj->strtab + obj->symtab[symnum].st_name,
205                                   obj->path, (void *)*where, defobj->path));
206                               break;
207 
208                     default:
209                               rdbg(("sym = %lu, type = %lu, offset = %p, "
210                                   "addend = %p, contents = %p, symbol = %s",
211                                   (u_long)ELF_R_SYM(rela->r_info),
212                                   (u_long)ELF_R_TYPE(rela->r_info),
213                                   (void *)rela->r_offset, (void *)rela->r_addend,
214                                   (void *)*where,
215                                   obj->strtab + obj->symtab[symnum].st_name));
216                               _rtld_error("%s: Unsupported relocation type %ld "
217                                   "in non-PLT relocations",
218                                   obj->path, (u_long) ELF_R_TYPE(rela->r_info));
219                               return -1;
220                     }
221           }
222           return 0;
223 }
224 
225 int
_rtld_relocate_plt_lazy(Obj_Entry * obj)226 _rtld_relocate_plt_lazy(Obj_Entry *obj)
227 {
228           const Elf_Rela *rela;
229 
230           if (!obj->relocbase)
231                     return 0;
232 
233           for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
234                     Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
235 
236                     assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT));
237 
238                     /* Just relocate the GOT slots pointing into the PLT */
239                     *where += (Elf_Addr)obj->relocbase;
240                     rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where));
241           }
242 
243           return 0;
244 }
245 
246 static inline int
_rtld_relocate_plt_object(const Obj_Entry * obj,const Elf_Rela * rela,Elf_Addr * tp)247 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela,
248     Elf_Addr *tp)
249 {
250           Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
251           Elf_Addr new_value;
252           const Elf_Sym  *def;
253           const Obj_Entry *defobj;
254           unsigned long info = rela->r_info;
255 
256           assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT));
257 
258           def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL);
259           if (__predict_false(def == NULL))
260                     return -1;
261           if (__predict_false(def == &_rtld_sym_zero))
262                     return 0;
263 
264           assert(rela->r_addend == 0);
265           if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
266                     if (tp == NULL)
267                               return 0;
268                     new_value = _rtld_resolve_ifunc(defobj, def);
269           } else {
270                     new_value = (Elf_Addr)(defobj->relocbase + def->st_value +
271                         rela->r_addend);
272           }
273           rdbg(("bind now/fixup in %s --> old=%p new=%p",
274               defobj->strtab + def->st_name, (void *)*where, (void *)new_value));
275           if (*where != new_value)
276                     *where = new_value;
277 
278           if (tp)
279                     *tp = new_value - rela->r_addend;
280 
281           return 0;
282 }
283 
284 caddr_t
_rtld_bind(const Obj_Entry * obj,Elf_Word reloff)285 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
286 {
287           const Elf_Rela *rela = (const Elf_Rela *)((const uint8_t *)obj->pltrela + reloff);
288           Elf_Addr result;
289           int err;
290 
291           result = 0;         /* XXX gcc */
292 
293           _rtld_shared_enter();
294           err = _rtld_relocate_plt_object(obj, rela, &result);
295           if (err)
296                     _rtld_die();
297           _rtld_shared_exit();
298 
299           return (caddr_t)result;
300 }
301 
302 int
_rtld_relocate_plt_objects(const Obj_Entry * obj)303 _rtld_relocate_plt_objects(const Obj_Entry *obj)
304 {
305           const Elf_Rela *rela;
306 
307           for (rela = obj->pltrela; rela < obj->pltrelalim; rela++)
308                     if (_rtld_relocate_plt_object(obj, rela, NULL) < 0)
309                               return -1;
310 
311           return 0;
312 }
313