process-pt-dynamic.c 23.3 KB
Newer Older
1
2
3
4
5
6
// Copyright © 2017 Collabora Ltd

// This file is part of libcapsule.

// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
Simon McVittie's avatar
Simon McVittie committed
7
// published by the Free Software Foundation; either version 2.1 of the
8
9
10
11
12
13
14
15
16
17
// License, or (at your option) any later version.

// libcapsule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule.  If not, see <http://www.gnu.org/licenses/>.

Simon McVittie's avatar
Simon McVittie committed
18
#include <assert.h>
19
#include <dlfcn.h>
20
#include <inttypes.h>
21
22
23
24
25
26
27
28
29
#include <stdlib.h>
#include <stdio.h>

#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

30
#include <capsule/capsule.h>
31
32
33
34
35
#include "utils.h"
#include "process-pt-dynamic.h"
#include "mmap-info.h"

static void *
36
#if __ELF_NATIVE_CLASS == 32
37
addr (void *base, ElfW(Addr) offset, ElfW(Sword) addend)
38
#elif __ELF_NATIVE_CLASS == 64
39
addr (void *base, ElfW(Addr) offset, ElfW(Sxword) addend)
40
41
42
#else
#error "Unsupported __ELF_NATIVE_CLASS size (not 32 or 64)"
#endif
43
{
44
    return base + offset + addend;
45
46
47
}


48
static int
49
50
try_relocation (ElfW(Addr) *reloc_addr, const char *name, void *data)
{
51
    capsule_item *map;
52
    relocation_data *rdata = data;
53
54
55
56
57
58
59
60
61

    if( !name || !*name || !reloc_addr )
        return 0;

    for( map = rdata->relocs; map->name; map++ )
    {
        if( strcmp( name, map->name ) )
            continue;

62
63
64
        DEBUG( DEBUG_RELOCS,
               "relocation for %s (%p->{ %p }, %p, %p)",
               name, reloc_addr, NULL, (void *)map->shim, (void *)map->real );
65

66
67
68
69
70
        // we used to check for the shim address here but it's possible
        // that we can't look it up if the proxy library was dlopen()ed
        // in which case map->shim will be null.
        // this turns out not to be a problem as we only need it when
        // working around RELRO linking, which doesn't apply to dlopen()
71
72
73
74
75

        // sought after symbols is not available in the private namespace
        if( !map->real )
        {
            rdata->count.failure++;
76
77
            DEBUG( DEBUG_RELOCS, "--failed" );

78
79
80
81
82
            return 1;
        }

        // our work here is already done, apparently
        if( *reloc_addr == map->real )
83
84
85
        {
            DEBUG( DEBUG_RELOCS, "==target %p already contains %p (%p)",
                   reloc_addr, (void *)*reloc_addr, (void *)map->real );
86
            return 0;
87
        }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
        // ======================================================================
        // exegesis:

        // linking goes like this: we start with a PLT entry pointing at the
        // 'trampoline' entry which patches up the relocations. The first
        // time we call a function, we go to the PLT which sends us to the
        // trampoline, which  finds the shim (in the case of our proxy library)
        // or the real address (in the case of a normal library) and pastes that
        // address into the PLT.

        // This function scribbles over the trampoline address with the real
        // address, thus bypassing the trampoline _and_ the shim permanently.

        /// IOW the 0th, 1st and second function calls normally look like this:
        // 0: function-call → PLT → trampoline : (PLT ← address) → address
        // 1: function-call → PLT → address
        // 2: ibid

        // If we are already pointing to the shim instead of the trampoline
        // that indicates we have RELRO linking - the linker has already resolved
        // the address to the shim (as it doesn't know about the real address
        // which is hidden inside the capsule).

        // -1: linker → function-lookup : (PLT ← address)
        //  0: function-call → PLT → address
        //  1: ibid

        // but⁰ RELRO linking also mprotect()s the relevant pages to be read-only
        // which prevents us from overwriting the address.

        // but¹ we are smarter than the average bear, and we tried to harvest
        // the mprotect info: If we did, then we will already have toggled the
        // write permission on everything that didn't have it and can proceed
        // (we're also not savages, so we'll put those permissions back later)

        // however, if we don't have any mprotect into for this relocation entry,
        // then we can't de-shim the RELROd PLT entry, and it's sad 🐼 time.
        // ======================================================================
        if( (*reloc_addr == map->shim) &&
            !find_mmap_info(rdata->mmap_info, reloc_addr) )
        {
129
130
            DEBUG( DEBUG_RELOCS|DEBUG_MPROTECT,
                   " ERROR: cannot update relocation record for %s", name );
131
132
133
134
135
            return 1; // FIXME - already shimmed, can't seem to override?
        }

        *reloc_addr = map->real;
        rdata->count.success++;
136
        DEBUG( DEBUG_RELOCS, "++relocated" );
137
138
139
140
141
142
143
        return 0;
    }

    // nothing to relocate
    return 0;
}

144
#if defined(__x86_64__)
145

146
147
148
149
150
static const char *
reloc_type_name (int type)
{
    switch (type)
    {
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
      // Please keep these in numerical order.

#define CASE(x) \
      case x: \
        return #x;

      CASE( R_X86_64_NONE )
      CASE( R_X86_64_64 )
      CASE( R_X86_64_PC32 )
      CASE( R_X86_64_GOT32 )
      CASE( R_X86_64_PLT32 )
      CASE( R_X86_64_COPY )
      CASE( R_X86_64_GLOB_DAT )
      CASE( R_X86_64_JUMP_SLOT )
      CASE( R_X86_64_RELATIVE )
      CASE( R_X86_64_GOTPCREL )
      CASE( R_X86_64_32 )
      CASE( R_X86_64_32S )
      CASE( R_X86_64_16 )
      CASE( R_X86_64_PC16 )
      CASE( R_X86_64_8 )
      CASE( R_X86_64_PC8 )
      CASE( R_X86_64_DTPMOD64 )
      CASE( R_X86_64_DTPOFF64 )
      CASE( R_X86_64_TPOFF64 )
      CASE( R_X86_64_TLSGD )
      CASE( R_X86_64_TLSLD )
      CASE( R_X86_64_DTPOFF32 )
      CASE( R_X86_64_GOTTPOFF )
      CASE( R_X86_64_TPOFF32 )
      CASE( R_X86_64_PC64 )
      CASE( R_X86_64_GOTOFF64 )
      CASE( R_X86_64_GOTPC32 )
      CASE( R_X86_64_GOT64 )
      CASE( R_X86_64_GOTPCREL64 )
      CASE( R_X86_64_GOTPC64 )
      CASE( R_X86_64_GOTPLT64 )
      CASE( R_X86_64_PLTOFF64 )
      CASE( R_X86_64_SIZE32 )
      CASE( R_X86_64_SIZE64 )
      CASE( R_X86_64_GOTPC32_TLSDESC )
      CASE( R_X86_64_TLSDESC_CALL )
      CASE( R_X86_64_TLSDESC )
      CASE( R_X86_64_IRELATIVE )

      // Entries below this point are new since glibc 2.19

#ifdef R_X86_64_RELATIVE64
      CASE( R_X86_64_RELATIVE64 )
#endif
#ifdef R_X64_64_GOTPCRELX
      CASE( R_X86_64_GOTPCRELX )
#endif
#ifdef R_X64_64_REX_GOTPCRELX
      CASE( R_X86_64_REX_GOTPCRELX )
#endif

#undef CASE

210
211
212
213
214
      default:
        return "UNKNOWN";
    }
}

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#elif defined(__i386__)

static const char *
reloc_type_name (int type)
{
    switch (type)
    {
      // Please keep these in numerical order.

#define CASE(x) \
      case x: \
        return #x;

      CASE( R_386_NONE )
      CASE( R_386_32 )
      CASE( R_386_PC32 )
      CASE( R_386_GOT32 )
      CASE( R_386_PLT32 )
      CASE( R_386_COPY )
      CASE( R_386_GLOB_DAT )
      CASE( R_386_JMP_SLOT )
      CASE( R_386_RELATIVE )
      CASE( R_386_GOTOFF )
      CASE( R_386_GOTPC )
      CASE( R_386_32PLT )
      CASE( R_386_TLS_TPOFF )
      CASE( R_386_TLS_IE )
      CASE( R_386_TLS_GOTIE )
      CASE( R_386_TLS_LE )
      CASE( R_386_TLS_GD )
      CASE( R_386_TLS_LDM )
      CASE( R_386_16 )
      CASE( R_386_PC16 )
      CASE( R_386_8 )
      CASE( R_386_PC8 )
      CASE( R_386_TLS_GD_32 )
      CASE( R_386_TLS_GD_PUSH )
      CASE( R_386_TLS_GD_CALL )
      CASE( R_386_TLS_GD_POP )
      CASE( R_386_TLS_LDM_32 )
      CASE( R_386_TLS_LDM_PUSH )
      CASE( R_386_TLS_LDM_CALL )
      CASE( R_386_TLS_LDM_POP )
      CASE( R_386_TLS_LDO_32 )
      CASE( R_386_TLS_IE_32 )
      CASE( R_386_TLS_LE_32 )
      CASE( R_386_TLS_DTPMOD32 )
      CASE( R_386_TLS_DTPOFF32 )
      CASE( R_386_TLS_TPOFF32 )
      CASE( R_386_SIZE32 )
      CASE( R_386_TLS_GOTDESC )
      CASE( R_386_TLS_DESC_CALL )
      CASE( R_386_TLS_DESC )
      CASE( R_386_IRELATIVE )

      // Entries below this point are new since glibc 2.19

#ifdef R_386_GOT32X
      CASE( R_386_GOT32X )
#endif

#undef CASE

      default:
        return "UNKNOWN";
    }
}

#else

#error Unsupported CPU architecture

#endif

289
int
290
process_dt_rela (const ElfW(Rela) *start,
Simon McVittie's avatar
Simon McVittie committed
291
                 size_t relasz,
292
                 const char *strtab,
Simon McVittie's avatar
Simon McVittie committed
293
                 size_t strsz,
294
                 const ElfW(Sym) *symtab,
Simon McVittie's avatar
Simon McVittie committed
295
                 size_t symsz,
296
                 void *base,
297
298
                 void *data)
{
299
    const ElfW(Rela) *entry;
300

Simon McVittie's avatar
Simon McVittie committed
301
302
303
304
305
306
307
308
309
    DEBUG( DEBUG_ELF,
           "%zu RELA entries (%zu bytes) starting at %p",
           relasz / sizeof(*entry),
           relasz,
           start );

    if( relasz % sizeof(*entry) != 0 )
        DEBUG( DEBUG_ELF, "%zu bytes left over?!", relasz % sizeof(*entry) );

310
    for( entry = start; entry < start + (relasz / sizeof(*entry)); entry++ )
311
312
313
    {
        int sym;
        int chr;
314
        const char *name = NULL;
315
316
        const ElfW(Sym) *symbol;

317
318
319
320
321
322
323
324
325
326
#if __ELF_NATIVE_CLASS == 32
        sym = ELF32_R_SYM (entry->r_info);
        chr = ELF32_R_TYPE(entry->r_info);
#elif __ELF_NATIVE_CLASS == 64
        sym = ELF64_R_SYM (entry->r_info);
        chr = ELF64_R_TYPE(entry->r_info);
#else
        fprintf( stderr, "__ELF_NATIVE_CLASS is neither 32 nor 64" );
        exit( 22 );
#endif
327

328
        DEBUG( DEBUG_ELF, "RELA entry at %p", entry );
329

330
        symbol = find_symbol( sym, symtab, symsz, strtab, strsz, &name );
331

332
333
        DEBUG( DEBUG_ELF,
               "symbol %p; name: %p:%s", symbol, name, name ? name : "-" );
334
335
336
337

        if( !symbol || !name || !*name )
            continue;

338
#if defined(__i386__) || defined(__x86_64__)
339
340
341
        switch( chr )
        {
            void *slot;
342
       // details at: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
343
344
345
346
347
#if defined(__i386__)
          case R_386_32:
          case R_386_GLOB_DAT:
          case R_386_JMP_SLOT:
#elif defined(__x86_64__)
348
          case R_X86_64_64:
349
          case R_X86_64_GLOB_DAT:
350
          case R_X86_64_JUMP_SLOT:
351
352
353
#else
#error Unsupported CPU architecture
#endif
354
            slot = addr( base, entry->r_offset, entry->r_addend );
355
            DEBUG( DEBUG_ELF,
356
357
                   " %30s %30s: %p ← { offset: %"FMT_ADDR"; add: %"FMT_SIZE" }",
                   name, reloc_type_name( chr ),
358
                   slot, entry->r_offset, entry->r_addend );
359
360
            try_relocation( slot, name, data );
            break;
361

362
          default:
363
364
365
366
            DEBUG( DEBUG_ELF,
                   "%s has slot type %s (%d), not doing anything special",
                   name, reloc_type_name( chr ), chr );
            break;
367
        }
368
369
370
#else
#error Unsupported CPU architecture
#endif
371
372
373
374
375
376
    }

    return 0;
}

int
377
process_dt_rel (const ElfW(Rel) *start,
Simon McVittie's avatar
Simon McVittie committed
378
                size_t relasz,
379
                const char *strtab,
Simon McVittie's avatar
Simon McVittie committed
380
                size_t strsz,
381
                const ElfW(Sym) *symtab,
Simon McVittie's avatar
Simon McVittie committed
382
                size_t symsz,
383
                void *base,
384
385
                void *data)
{
386
    const ElfW(Rel) *entry;
387

Simon McVittie's avatar
Simon McVittie committed
388
389
390
391
392
393
394
395
396
    DEBUG( DEBUG_ELF,
           "%zu REL entries (%zu bytes) starting at %p",
           relasz / sizeof(*entry),
           relasz,
           start );

    if( relasz % sizeof(*entry) != 0 )
        DEBUG( DEBUG_ELF, "%zu bytes left over?!", relasz % sizeof(*entry) );

397
    for( entry = start; entry < start + (relasz / sizeof(*entry)); entry++ )
398
399
400
    {
        int sym;
        int chr;
401
        const char *name = NULL;
402
403
404

        const ElfW(Sym) *symbol;

405
406
407
408
409
410
411
#if __ELF_NATIVE_CLASS == 32
        sym = ELF32_R_SYM (entry->r_info);
        chr = ELF32_R_TYPE(entry->r_info);
#elif __ELF_NATIVE_CLASS == 64
        sym = ELF64_R_SYM (entry->r_info);
        chr = ELF64_R_TYPE(entry->r_info);
#else
412
#error Unsupported CPU architecture
413
#endif
414

415
        symbol = find_symbol( sym, symtab, symsz, strtab, strsz, &name );
416

417
418
419
        DEBUG( DEBUG_ELF,
               "symbol %p; name: %p:%s", symbol, name, name ? name : "-" );

420
421
422
        if( !symbol || !name || !*name )
            continue;

423
#if defined(__i386__) || defined(__x86_64__)
424
425
426
        switch( chr )
        {
            void *slot;
427
       // details at: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
428
429
430
431
432
#if defined(__i386__)
          case R_386_32:
          case R_386_GLOB_DAT:
          case R_386_JMP_SLOT:
#elif defined(__x86_64__)
433
          case R_X86_64_64:
434
          case R_X86_64_GLOB_DAT:
435
          case R_X86_64_JUMP_SLOT:
436
437
438
#else
#error Unsupported CPU architecture
#endif
439
            slot = addr( base, entry->r_offset, 0 );
440
            DEBUG( DEBUG_ELF,
441
442
                   " %30s %30s: %p ← { offset: %"FMT_ADDR"; addend: n/a }",
                   name,
443
444
                   reloc_type_name( chr ),
                   slot, entry->r_offset );
445
446
            try_relocation( slot, name, data );
            break;
447
448

          default:
449
450
451
            DEBUG( DEBUG_ELF,
                   "%s has slot type %s (%d), not doing anything special",
                   name, reloc_type_name( chr ), chr );
452
            break;
453
        }
454
455
456
#else
#error Unsupported CPU architecture
#endif
457
458
459
460
461
    }

    return 0;
}

Simon McVittie's avatar
Simon McVittie committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/*
 * process_pt_dynamic:
 * @start: offset of dynamic section (an array of ElfW(Dyn) structures)
 *  relative to @base
 * @size: size of dynamic section in bytes (not structs!), or 0
 *  if the dynamic section is terminated by an entry with d_tag == DT_NULL
 * @base: Starting address of the program header (the shared object)
 *  in memory. @start is relative to this. Addresses are normally
 *  relative to this, except for when they are absolute (see fix_addr()).
 * @process_rela: called when we find the DT_RELA section
 * @process_rel: called when we find the DT_REL section
 * @data: arbitrary user data to be passed to both @process_rela
 *  and @process_rel
 *
 * Iterate over the PT_DYNAMIC entry in a shared library and perform
 * relocations using the given callbacks.
 */
479
int
480
process_pt_dynamic (ElfW(Addr) start,
481
                    size_t size,
482
                    void *base,
483
484
                    relocate_rela_cb process_rela,
                    relocate_rel_cb process_rel,
485
486
487
                    void *data)
{
    int ret = 0;
488
489
    const ElfW(Dyn) *entries;
    const ElfW(Dyn) *entry;
Simon McVittie's avatar
Simon McVittie committed
490
491
492
    size_t relasz   = (size_t) -1;
    size_t relsz    = (size_t) -1;
    size_t jmprelsz = (size_t) -1;
493
    int jmpreltype = DT_NULL;
494
    const ElfW(Sym) *symtab = NULL;
Simon McVittie's avatar
Simon McVittie committed
495
496
497
    size_t strsz = (size_t) -1;
    const char *strtab = dynamic_section_find_strtab( base + start, base, &strsz );
    size_t symsz;
498

499
    DEBUG( DEBUG_ELF,
500
501
502
503
           "start: %#" PRIxPTR "; size: %" FMT_SIZE "; base: %p; handlers: %p %p; …",
           start, size, base, process_rela, process_rel );
    entries = base + start;
    DEBUG( DEBUG_ELF, "dyn entry: %p", entries );
504
505

    DEBUG( DEBUG_ELF,
506
           "strtab is at %p: %s", strtab, strtab ? "…" : "");
507

508
    // Do a first pass to find the bits we'll need later
509
    for( entry = entries;
510
511
         (entry->d_tag != DT_NULL) &&
           ((size == 0) || ((void *)entry < (start + base + size)));
512
         entry++ ) {
513
514
515
516
        switch( entry->d_tag )
        {
          case DT_PLTRELSZ:
            jmprelsz = entry->d_un.d_val;
Simon McVittie's avatar
Simon McVittie committed
517
            DEBUG( DEBUG_ELF, "jmprelsz is %zu", jmprelsz );
518
519
520
            break;

          case DT_SYMTAB:
521
            symtab = fix_addr( base, entry->d_un.d_ptr );
522
            DEBUG( DEBUG_ELF, "symtab is %p", symtab );
523
524
            break;

525
526
          case DT_RELASZ:
            relasz = entry->d_un.d_val;
Simon McVittie's avatar
Simon McVittie committed
527
            DEBUG( DEBUG_ELF, "relasz is %zu", relasz );
528
529
            break;

530
531
          case DT_RELSZ:
            relsz = entry->d_un.d_val;
Simon McVittie's avatar
Simon McVittie committed
532
            DEBUG( DEBUG_ELF, "relsz is %zu", relsz );
533
534
            break;

535
536
537
538
539
540
541
542
543
544
545
546
547
          case DT_PLTREL:
            jmpreltype = entry->d_un.d_val;
            DEBUG( DEBUG_ELF, "jmpreltype is %d : %s", jmpreltype,
                   jmpreltype == DT_REL  ? "DT_REL"  :
                   jmpreltype == DT_RELA ? "DT_RELA" : "???" );
            break;

          default:
            // We'll deal with this later
            break;
        }
    }

Simon McVittie's avatar
Simon McVittie committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
    /* XXX Apparently the only way to find out the size of the dynamic
       symbol section is to assume that the string table follows right
       afterwards... —glibc elf/dl-fptr.c */
    assert( strtab >= (const char *) symtab );
    symsz = strtab - (const char *) symtab;

    DEBUG( DEBUG_ELF,
           "%zu symbol table entries (%zu bytes) starting at %p",
           symsz / sizeof(*symtab),
           symsz,
           symtab );

    if( symsz % sizeof(*symtab) != 0 )
        DEBUG( DEBUG_ELF, "%zu bytes left over?!", symsz % sizeof(*symtab) );

563
    for( entry = entries;
564
565
566
567
568
         (entry->d_tag != DT_NULL) &&
           ((size == 0) || ((void *)entry < (start + base + size)));
         entry++ ) {
        switch( entry->d_tag )
        {
569
570
571
572
573
574
575
576
          // Please keep the contents of this switch in numerical order.

          // IGNORE(x): Ignore a known d_tag that we believe we don't need
          // to know about
#define IGNORE(x) \
          case x: \
            DEBUG( DEBUG_ELF, "ignoring %s (0x%zx): 0x%zx", \
                   #x, (size_t) x, (size_t) entry->d_un.d_val); \
577
578
            break;

579
580
581
582
583
584
585
586
587
588
589
590
591
          // ALREADY_DID(x): Silently ignore a case we dealt with in
          // the previous loop
#define ALREADY_DID(x) \
          case x: \
            break;

          IGNORE( DT_NEEDED )
          ALREADY_DID( DT_PLTRELSZ )
          IGNORE( DT_PLTGOT )
          IGNORE( DT_HASH )
          IGNORE( DT_STRTAB )
          ALREADY_DID( DT_SYMTAB )

592
593
594
          case DT_RELA:
            if( process_rela != NULL )
            {
595
596
                const ElfW(Rela) *relstart;

597
                DEBUG( DEBUG_ELF, "processing DT_RELA section" );
Simon McVittie's avatar
Simon McVittie committed
598
                if( relasz == (size_t) -1 )
599
600
601
602
                {
                    fprintf( stderr, "libcapsule: DT_RELA section not accompanied by DT_RELASZ, ignoring" );
                    break;
                }
603
                relstart = fix_addr( base, entry->d_un.d_ptr );
Simon McVittie's avatar
Simon McVittie committed
604
                process_rela( relstart, relasz, strtab, strsz, symtab, symsz, base, data );
605
606
607
            }
            else
            {
608
                DEBUG( DEBUG_ELF,
609
                       "skipping DT_RELA section: no handler" );
610
611
612
            }
            break;

613
614
615
616
617
618
619
620
621
622
          ALREADY_DID( DT_RELASZ )
          IGNORE( DT_RELAENT )
          IGNORE( DT_STRSZ )
          IGNORE( DT_SYMENT )
          IGNORE( DT_INIT )
          IGNORE( DT_FINI )
          IGNORE( DT_SONAME )
          IGNORE( DT_RPATH )
          IGNORE( DT_SYMBOLIC )

623
624
625
626
627
628
          case DT_REL:
            if( process_rel != NULL )
            {
                const ElfW(Rel) *relstart;

                DEBUG( DEBUG_ELF, "processing DT_REL section" );
Simon McVittie's avatar
Simon McVittie committed
629
                if( relsz == (size_t) -1 )
630
631
632
633
634
                {
                    fprintf( stderr, "libcapsule: DT_REL section not accompanied by DT_RELSZ, ignoring" );
                    break;
                }
                relstart = fix_addr( base, entry->d_un.d_ptr );
Simon McVittie's avatar
Simon McVittie committed
635
                process_rel( relstart, relsz, strtab, strsz, symtab, symsz, base, data );
636
637
638
639
640
641
642
            }
            else
            {
                DEBUG( DEBUG_ELF, "skipping DT_REL section: no handler" );
            }
            break;

643
644
645
646
647
648
          ALREADY_DID( DT_RELSZ )
          IGNORE( DT_RELENT )
          ALREADY_DID( DT_PLTREL )
          IGNORE( DT_DEBUG )
          IGNORE( DT_TEXTREL )

649
          case DT_JMPREL:
Simon McVittie's avatar
Simon McVittie committed
650
            if( jmprelsz == (size_t) -1 )
651
652
653
654
655
            {
                fprintf( stderr, "libcapsule: DT_JMPREL section not accompanied by DT_PLTRELSZ, ignoring" );
                break;
            }

656
            if( jmpreltype == DT_NULL )
657
658
659
660
            {
                fprintf( stderr, "libcapsule: DT_JMPREL section not accompanied by DT_PLTREL, ignoring" );
                break;
            }
661
662
663
664
665
666

            switch( jmpreltype )
            {
              case DT_REL:
                if( process_rel != NULL )
                {
667
668
                    const ElfW(Rel) *relstart;

669
                    DEBUG( DEBUG_ELF,
670
                           "processing DT_JMPREL/DT_REL section" );
671
                    relstart = fix_addr( base, entry->d_un.d_ptr );
672
                    DEBUG( DEBUG_ELF, "  -> REL entry #0 at %p", relstart );
Simon McVittie's avatar
Simon McVittie committed
673
674
                    ret = process_rel( relstart, jmprelsz, strtab, strsz,
                                       symtab, symsz, base, data );
675
676
677
                }
                else
                {
678
                    DEBUG( DEBUG_ELF,
679
                           "skipping DT_JMPREL/DT_REL section: no handler" );
680
681
682
683
684
685
                }
                break;

              case DT_RELA:
                if( process_rela != NULL )
                {
686
687
                    const ElfW(Rela) *relstart;

688
689
                    DEBUG( DEBUG_ELF,
                           "processing DT_JMPREL/DT_RELA section" );
690
                    relstart = fix_addr( base, entry->d_un.d_ptr );
Simon McVittie's avatar
Simon McVittie committed
691
692
                    ret = process_rela( relstart, jmprelsz, strtab, strsz,
                                        symtab, symsz, base, data );
693
694
695
                }
                else
                {
696
697
                    DEBUG( DEBUG_ELF,
                           "skipping DT_JMPREL/DT_RELA section: no handler" );
698
699
700
701
                }
                break;

              default:
702
                DEBUG( DEBUG_ELF,
703
704
                       "Unknown DT_PLTREL value: %d (expected %d or %d)",
                       jmpreltype, DT_REL, DT_RELA );
705
706
707
                ret = 1;
                break;
            }
708

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
          IGNORE( DT_BIND_NOW )
          IGNORE( DT_INIT_ARRAY )
          IGNORE( DT_FINI_ARRAY )
          IGNORE( DT_INIT_ARRAYSZ )
          IGNORE( DT_FINI_ARRAYSZ )
          IGNORE( DT_RUNPATH )
          IGNORE( DT_FLAGS )
          // DT_ENCODING is numerically equal to DT_PREINIT_ARRAY
          // so we can't separate them
          case DT_ENCODING:
            DEBUG( DEBUG_ELF,
                   "ignoring DT_ENCODING or DT_PREINIT_ARRAY (0x%zx): 0x%zx",
                   (size_t) DT_ENCODING, (size_t) entry->d_un.d_val); \
            break;
          IGNORE( DT_PREINIT_ARRAYSZ )
          IGNORE( DT_NUM )

          // OS-specifics, in range DT_LOOS to DT_HIOS

          // Uses Dyn.d_un.d_val
          IGNORE( DT_GNU_PRELINKED )
          IGNORE( DT_GNU_CONFLICTSZ )
          IGNORE( DT_GNU_LIBLISTSZ )
          IGNORE( DT_CHECKSUM )
          IGNORE( DT_PLTPADSZ )
          IGNORE( DT_MOVEENT )
          IGNORE( DT_MOVESZ )
          IGNORE( DT_FEATURE_1 )
          IGNORE( DT_POSFLAG_1 )
          IGNORE( DT_SYMINSZ )
          IGNORE( DT_SYMINENT )

          // Uses Dyn.d_un.d_ptr
          IGNORE( DT_GNU_HASH )
          IGNORE( DT_TLSDESC_PLT )
          IGNORE( DT_TLSDESC_GOT )
          IGNORE( DT_GNU_CONFLICT )
          IGNORE( DT_GNU_LIBLIST )
          IGNORE( DT_CONFIG )
          IGNORE( DT_DEPAUDIT )
          IGNORE( DT_AUDIT )
          IGNORE( DT_PLTPAD )
          IGNORE( DT_MOVETAB )
          IGNORE( DT_SYMINFO )

          IGNORE( DT_VERSYM )

          IGNORE( DT_RELACOUNT )
          IGNORE( DT_RELCOUNT )

          // Sun-compatible
          IGNORE( DT_FLAGS_1 )
          IGNORE( DT_VERDEF )
          IGNORE( DT_VERDEFNUM )
          IGNORE( DT_VERNEED )
          IGNORE( DT_VERNEEDNUM )

          // Sun-compatible
          IGNORE( DT_AUXILIARY )
          IGNORE( DT_FILTER )

#undef IGNORE
#undef ALREADY_DID

773
          default:
774
775
            DEBUG( DEBUG_ELF, "Ignoring unknown dynamic section entry tag 0x%zx",
                   (size_t) entry->d_tag );
776
            break;
777
        }
778
    }
779
780
781
782

    return ret;
}