Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/rtld-elf/amd64/reloc.c
34923 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright 1996, 1997, 1998, 1999 John D. Polstra.
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
/*
29
* Dynamic linker for ELF.
30
*
31
* John Polstra <[email protected]>.
32
*/
33
34
#define _WANT_P_OSREL
35
#include <sys/param.h>
36
#include <sys/mman.h>
37
38
#include <machine/cpufunc.h>
39
#include <machine/specialreg.h>
40
#include <machine/sysarch.h>
41
42
#include <dlfcn.h>
43
#include <err.h>
44
#include <errno.h>
45
#include <fcntl.h>
46
#include <stdarg.h>
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
#include <unistd.h>
51
52
#include "debug.h"
53
#include "rtld.h"
54
#include "rtld_tls.h"
55
56
/*
57
* Process the special R_X86_64_COPY relocations in the main program. These
58
* copy data from a shared object into a region in the main program's BSS
59
* segment.
60
*
61
* Returns 0 on success, -1 on failure.
62
*/
63
int
64
do_copy_relocations(Obj_Entry *dstobj)
65
{
66
const Elf_Rela *relalim;
67
const Elf_Rela *rela;
68
69
assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
70
71
relalim = (const Elf_Rela *)((const char *)dstobj->rela +
72
dstobj->relasize);
73
for (rela = dstobj->rela; rela < relalim; rela++) {
74
if (ELF_R_TYPE(rela->r_info) == R_X86_64_COPY) {
75
void *dstaddr;
76
const Elf_Sym *dstsym;
77
const char *name;
78
size_t size;
79
const void *srcaddr;
80
const Elf_Sym *srcsym;
81
const Obj_Entry *srcobj, *defobj;
82
SymLook req;
83
int res;
84
85
dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
86
dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
87
name = dstobj->strtab + dstsym->st_name;
88
size = dstsym->st_size;
89
symlook_init(&req, name);
90
req.ventry = fetch_ventry(dstobj,
91
ELF_R_SYM(rela->r_info));
92
req.flags = SYMLOOK_EARLY;
93
94
for (srcobj = globallist_next(dstobj); srcobj != NULL;
95
srcobj = globallist_next(srcobj)) {
96
res = symlook_obj(&req, srcobj);
97
if (res == 0) {
98
srcsym = req.sym_out;
99
defobj = req.defobj_out;
100
break;
101
}
102
}
103
104
if (srcobj == NULL) {
105
_rtld_error(
106
"Undefined symbol \"%s\" referenced from COPY relocation in %s",
107
name, dstobj->path);
108
return (-1);
109
}
110
111
srcaddr = (const void *)(defobj->relocbase +
112
srcsym->st_value);
113
memcpy(dstaddr, srcaddr, size);
114
}
115
}
116
117
return (0);
118
}
119
120
/* Initialize the special GOT entries. */
121
void
122
init_pltgot(Obj_Entry *obj)
123
{
124
if (obj->pltgot != NULL) {
125
obj->pltgot[1] = (Elf_Addr)obj;
126
obj->pltgot[2] = (Elf_Addr)&_rtld_bind_start;
127
}
128
}
129
130
/* Process the non-PLT relocations. */
131
int
132
reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
133
RtldLockState *lockstate)
134
{
135
const Elf_Rela *relalim;
136
const Elf_Rela *rela;
137
SymCache *cache;
138
const Elf_Sym *def;
139
const Obj_Entry *defobj;
140
Elf_Addr *where, symval;
141
Elf32_Addr *where32;
142
int r;
143
144
r = -1;
145
symval = 0;
146
def = NULL;
147
148
/*
149
* The dynamic loader may be called from a thread, we have
150
* limited amounts of stack available so we cannot use alloca().
151
*/
152
if (obj != obj_rtld) {
153
cache = calloc(obj->dynsymcount, sizeof(SymCache));
154
/* No need to check for NULL here */
155
} else
156
cache = NULL;
157
158
relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
159
for (rela = obj->rela; rela < relalim; rela++) {
160
/*
161
* First, resolve symbol for relocations which
162
* reference symbols.
163
*/
164
switch (ELF_R_TYPE(rela->r_info)) {
165
case R_X86_64_64:
166
case R_X86_64_PC32:
167
case R_X86_64_GLOB_DAT:
168
case R_X86_64_TPOFF64:
169
case R_X86_64_TPOFF32:
170
case R_X86_64_DTPMOD64:
171
case R_X86_64_DTPOFF64:
172
case R_X86_64_DTPOFF32:
173
def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
174
flags, cache, lockstate);
175
if (def == NULL)
176
goto done;
177
178
/*
179
* If symbol is IFUNC, only perform relocation
180
* when caller allowed it by passing
181
* SYMLOOK_IFUNC flag. Skip the relocations
182
* otherwise.
183
*
184
* Also error out in case IFUNC relocations
185
* are specified for TLS, which cannot be
186
* usefully interpreted.
187
*/
188
if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
189
switch (ELF_R_TYPE(rela->r_info)) {
190
case R_X86_64_64:
191
case R_X86_64_PC32:
192
case R_X86_64_GLOB_DAT:
193
if ((flags & SYMLOOK_IFUNC) == 0) {
194
obj->non_plt_gnu_ifunc = true;
195
continue;
196
}
197
symval = (Elf_Addr)rtld_resolve_ifunc(
198
defobj, def);
199
break;
200
case R_X86_64_TPOFF64:
201
case R_X86_64_TPOFF32:
202
case R_X86_64_DTPMOD64:
203
case R_X86_64_DTPOFF64:
204
case R_X86_64_DTPOFF32:
205
_rtld_error("%s: IFUNC for TLS reloc",
206
obj->path);
207
goto done;
208
}
209
} else {
210
if ((flags & SYMLOOK_IFUNC) != 0)
211
continue;
212
symval = (Elf_Addr)defobj->relocbase +
213
def->st_value;
214
}
215
break;
216
default:
217
if ((flags & SYMLOOK_IFUNC) != 0)
218
continue;
219
break;
220
}
221
where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
222
where32 = (Elf32_Addr *)where;
223
224
switch (ELF_R_TYPE(rela->r_info)) {
225
case R_X86_64_NONE:
226
break;
227
case R_X86_64_64:
228
*where = symval + rela->r_addend;
229
break;
230
case R_X86_64_PC32:
231
/*
232
* I don't think the dynamic linker should
233
* ever see this type of relocation. But the
234
* binutils-2.6 tools sometimes generate it.
235
*/
236
*where32 = (Elf32_Addr)(unsigned long)(symval +
237
rela->r_addend - (Elf_Addr)where);
238
break;
239
/* missing: R_X86_64_GOT32 R_X86_64_PLT32 */
240
case R_X86_64_COPY:
241
/*
242
* These are deferred until all other
243
* relocations have been done. All we do here
244
* is make sure that the COPY relocation is
245
* not in a shared library. They are allowed
246
* only in executable files.
247
*/
248
if (!obj->mainprog) {
249
_rtld_error(
250
"%s: Unexpected R_X86_64_COPY relocation in shared library",
251
obj->path);
252
goto done;
253
}
254
break;
255
case R_X86_64_GLOB_DAT:
256
*where = symval;
257
break;
258
case R_X86_64_TPOFF64:
259
/*
260
* We lazily allocate offsets for static TLS
261
* as we see the first relocation that
262
* references the TLS block. This allows us to
263
* support (small amounts of) static TLS in
264
* dynamically loaded modules. If we run out
265
* of space, we generate an error.
266
*/
267
if (!defobj->tls_static) {
268
if (!allocate_tls_offset(__DECONST(Obj_Entry *,
269
defobj))) {
270
_rtld_error(
271
"%s: No space available for static Thread Local Storage",
272
obj->path);
273
goto done;
274
}
275
}
276
*where = (Elf_Addr)(def->st_value - defobj->tlsoffset +
277
rela->r_addend);
278
break;
279
case R_X86_64_TPOFF32:
280
/*
281
* We lazily allocate offsets for static TLS
282
* as we see the first relocation that
283
* references the TLS block. This allows us to
284
* support (small amounts of) static TLS in
285
* dynamically loaded modules. If we run out
286
* of space, we generate an error.
287
*/
288
if (!defobj->tls_static) {
289
if (!allocate_tls_offset(__DECONST(Obj_Entry *,
290
defobj))) {
291
_rtld_error(
292
"%s: No space available for static Thread Local Storage",
293
obj->path);
294
goto done;
295
}
296
}
297
*where32 = (Elf32_Addr)(def->st_value -
298
defobj->tlsoffset + rela->r_addend);
299
break;
300
case R_X86_64_DTPMOD64:
301
*where += (Elf_Addr)defobj->tlsindex;
302
break;
303
case R_X86_64_DTPOFF64:
304
*where += (Elf_Addr)(def->st_value + rela->r_addend);
305
break;
306
case R_X86_64_DTPOFF32:
307
*where32 += (Elf32_Addr)(def->st_value +
308
rela->r_addend);
309
break;
310
case R_X86_64_RELATIVE:
311
*where = (Elf_Addr)(obj->relocbase + rela->r_addend);
312
break;
313
case R_X86_64_IRELATIVE:
314
obj->irelative_nonplt = true;
315
break;
316
317
/*
318
* missing:
319
* R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16,
320
* R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8
321
*/
322
default:
323
_rtld_error(
324
"%s: Unsupported relocation type %u in non-PLT relocations",
325
obj->path, (unsigned int)ELF_R_TYPE(rela->r_info));
326
goto done;
327
}
328
}
329
r = 0;
330
done:
331
free(cache);
332
return (r);
333
}
334
335
/* Process the PLT relocations. */
336
int
337
reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
338
{
339
const Elf_Rela *relalim;
340
const Elf_Rela *rela;
341
342
relalim = (const Elf_Rela *)((const char *)obj->pltrela +
343
obj->pltrelasize);
344
for (rela = obj->pltrela; rela < relalim; rela++) {
345
Elf_Addr *where;
346
347
switch (ELF_R_TYPE(rela->r_info)) {
348
case R_X86_64_JMP_SLOT:
349
/* Relocate the GOT slot pointing into the PLT. */
350
where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
351
*where += (Elf_Addr)obj->relocbase;
352
break;
353
354
case R_X86_64_IRELATIVE:
355
obj->irelative = true;
356
break;
357
358
default:
359
_rtld_error("Unknown relocation type %x in PLT",
360
(unsigned int)ELF_R_TYPE(rela->r_info));
361
return (-1);
362
}
363
}
364
return (0);
365
}
366
367
/* Relocate the jump slots in an object. */
368
int
369
reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
370
{
371
const Elf_Rela *relalim;
372
const Elf_Rela *rela;
373
374
if (obj->jmpslots_done)
375
return (0);
376
relalim = (const Elf_Rela *)((const char *)obj->pltrela +
377
obj->pltrelasize);
378
for (rela = obj->pltrela; rela < relalim; rela++) {
379
Elf_Addr *where, target;
380
const Elf_Sym *def;
381
const Obj_Entry *defobj;
382
383
switch (ELF_R_TYPE(rela->r_info)) {
384
case R_X86_64_JMP_SLOT:
385
where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
386
def = find_symdef(ELF_R_SYM(rela->r_info), obj,
387
&defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
388
if (def == NULL)
389
return (-1);
390
if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
391
obj->gnu_ifunc = true;
392
continue;
393
}
394
target = (Elf_Addr)(defobj->relocbase + def->st_value +
395
rela->r_addend);
396
reloc_jmpslot(where, target, defobj, obj,
397
(const Elf_Rel *)rela);
398
break;
399
400
case R_X86_64_IRELATIVE:
401
break;
402
403
default:
404
_rtld_error("Unknown relocation type %x in PLT",
405
(unsigned int)ELF_R_TYPE(rela->r_info));
406
return (-1);
407
}
408
}
409
obj->jmpslots_done = true;
410
return (0);
411
}
412
413
/* Fixup the jump slot at "where" to transfer control to "target". */
414
Elf_Addr
415
reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
416
const struct Struct_Obj_Entry *obj __unused,
417
const struct Struct_Obj_Entry *refobj __unused, const Elf_Rel *rel __unused)
418
{
419
dbg("reloc_jmpslot: *%p = %p", where, (void *)target);
420
if (!ld_bind_not)
421
*where = target;
422
return (target);
423
}
424
425
static void
426
reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela,
427
RtldLockState *lockstate)
428
{
429
Elf_Addr *where, target, *ptr;
430
431
ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
432
where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
433
lock_release(rtld_bind_lock, lockstate);
434
target = call_ifunc_resolver(ptr);
435
wlock_acquire(rtld_bind_lock, lockstate);
436
*where = target;
437
}
438
439
int
440
reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
441
{
442
const Elf_Rela *relalim;
443
const Elf_Rela *rela;
444
445
if (!obj->irelative)
446
return (0);
447
obj->irelative = false;
448
relalim = (const Elf_Rela *)((const char *)obj->pltrela +
449
obj->pltrelasize);
450
for (rela = obj->pltrela; rela < relalim; rela++) {
451
if (ELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE)
452
reloc_iresolve_one(obj, rela, lockstate);
453
}
454
return (0);
455
}
456
457
int
458
reloc_iresolve_nonplt(Obj_Entry *obj, RtldLockState *lockstate)
459
{
460
const Elf_Rela *relalim;
461
const Elf_Rela *rela;
462
463
if (!obj->irelative_nonplt)
464
return (0);
465
obj->irelative_nonplt = false;
466
relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
467
for (rela = obj->rela; rela < relalim; rela++) {
468
if (ELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE)
469
reloc_iresolve_one(obj, rela, lockstate);
470
}
471
return (0);
472
}
473
474
int
475
reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate)
476
{
477
const Elf_Rela *relalim;
478
const Elf_Rela *rela;
479
480
if (!obj->gnu_ifunc)
481
return (0);
482
relalim = (const Elf_Rela *)((const char *)obj->pltrela +
483
obj->pltrelasize);
484
for (rela = obj->pltrela; rela < relalim; rela++) {
485
Elf_Addr *where, target;
486
const Elf_Sym *def;
487
const Obj_Entry *defobj;
488
489
switch (ELF_R_TYPE(rela->r_info)) {
490
case R_X86_64_JMP_SLOT:
491
where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
492
def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
493
SYMLOOK_IN_PLT | flags, NULL, lockstate);
494
if (def == NULL)
495
return (-1);
496
if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
497
continue;
498
lock_release(rtld_bind_lock, lockstate);
499
target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
500
wlock_acquire(rtld_bind_lock, lockstate);
501
reloc_jmpslot(where, target, defobj, obj,
502
(const Elf_Rel *)rela);
503
break;
504
}
505
}
506
obj->gnu_ifunc = false;
507
return (0);
508
}
509
510
uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2;
511
512
void
513
ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
514
{
515
u_int p[4], cpu_high;
516
517
do_cpuid(1, p);
518
cpu_feature = p[3];
519
cpu_feature2 = p[2];
520
do_cpuid(0, p);
521
cpu_high = p[0];
522
if (cpu_high >= 7) {
523
cpuid_count(7, 0, p);
524
cpu_stdext_feature = p[1];
525
cpu_stdext_feature2 = p[2];
526
}
527
}
528
529
int __getosreldate(void);
530
531
void
532
allocate_initial_tls(Obj_Entry *objs)
533
{
534
void *addr;
535
536
/*
537
* Fix the size of the static TLS block by using the maximum
538
* offset allocated so far and adding a bit for dynamic
539
* modules to use.
540
*/
541
tls_static_space = tls_last_offset + ld_static_tls_extra;
542
543
addr = allocate_tls(objs, 0, TLS_TCB_SIZE, TLS_TCB_ALIGN);
544
545
/*
546
* This does not use _tcb_set() as it calls amd64_set_tlsbase()
547
* which is an ifunc and rtld must not use ifuncs.
548
*/
549
if (__getosreldate() >= P_OSREL_TLSBASE)
550
sysarch(AMD64_SET_TLSBASE, &addr);
551
else if ((cpu_stdext_feature & CPUID_STDEXT_FSGSBASE) != 0)
552
wrfsbase((uintptr_t)addr);
553
else
554
sysarch(AMD64_SET_FSBASE, &addr);
555
}
556
557
void *
558
__tls_get_addr(tls_index *ti)
559
{
560
return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset));
561
}
562
563
size_t
564
calculate_tls_offset(size_t prev_offset, size_t prev_size __unused, size_t size,
565
size_t align, size_t offset)
566
{
567
size_t res;
568
569
/*
570
* res is the smallest integer satisfying res - prev_offset >= size
571
* and (-res) % p_align = p_vaddr % p_align (= p_offset % p_align).
572
*/
573
res = prev_offset + size + align - 1;
574
res -= (res + offset) & (align - 1);
575
return (res);
576
}
577
578
size_t
579
calculate_first_tls_offset(size_t size, size_t align, size_t offset)
580
{
581
return (calculate_tls_offset(0, 0, size, align, offset));
582
}
583
584