File: reloc32.c

package info (click to toggle)
memtest86%2B 6.10-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,532 kB
  • sloc: ansic: 19,267; asm: 2,027; makefile: 367; sh: 134
file content (167 lines) | stat: -rw-r--r-- 4,874 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020-2022 Martin Whitaker.
//
// Derived from memtest86+ reloc.c:
//
// reloc.c - MemTest-86  Version 3.3
//
// Released under version 2 of the Gnu Public License.
// By Eric Biederman

#include <stddef.h>
#include <stdint.h>

#include "assert.h"

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

// Dynamic section tag values

#define DT_NULL         0           // End of dynamic section
#define DT_PLTRELSZ     2           // Size in bytes of PLT relocs
#define DT_REL          17          // Address of Rel relocs
#define DT_RELSZ        18          // Total size of Rel relocs
#define DT_RELENT	19
#define DT_PLTREL       20          // Type of reloc in PLT
#define DT_JMPREL       23          // Address of PLT relocs
#define DT_NUM          34          // Number of tag values

// Relocation types

#define R_386_NONE      0
#define R_386_RELATIVE  8

//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------

typedef uint32_t    Elf32_Addr;
typedef int32_t     Elf32_Sword;
typedef uint32_t    Elf32_Word;

typedef struct
{
    Elf32_Sword     d_tag;
    union
    {
        Elf32_Word  d_val;
        Elf32_Addr  d_ptr;
    } d_un;
} Elf32_Dyn;

typedef struct
{
    Elf32_Addr      r_offset;
    Elf32_Word      r_info;
} Elf32_Rel;

//------------------------------------------------------------------------------
// Private Functions
//------------------------------------------------------------------------------

#define ELF32_R_TYPE(r_info)    ((r_info) & 0xff)

/*
 * Return the run-time load address of the shared object. This must be inlined
 * in a function which uses global data.
 */
static inline Elf32_Addr __attribute__ ((unused)) get_load_address(void)
{
    Elf32_Addr addr;
    __asm__ __volatile__ (
        "leal _start@GOTOFF(%%ebx), %0"
        : "=r" (addr)
        :
        : "cc"
    );
    return addr;
}

/*
 * Return the link-time address of _DYNAMIC. Conveniently, this is the first
 * element of the GOT. This must be inlined in a function which uses global
 * data.
 */
static inline Elf32_Addr __attribute__ ((unused)) get_dynamic_section_offset(void)
{
    register Elf32_Addr *got __asm__ ("%ebx");
    return *got;
}

static void get_dynamic_info(Elf32_Dyn *dyn_section, Elf32_Addr load_offs, Elf32_Dyn *dyn_info[DT_NUM])
{
    Elf32_Dyn *dyn = dyn_section;
    while (dyn->d_tag != DT_NULL) {
        if (dyn->d_tag < DT_NUM) {
            dyn_info[dyn->d_tag] = dyn;
        }
        dyn++;
    }

    if (dyn_info[DT_REL] != NULL) {
        assert(dyn_info[DT_RELENT]->d_un.d_val == sizeof(Elf32_Rel));
        dyn_info[DT_REL]->d_un.d_ptr += load_offs;
    }
    if (dyn_info[DT_PLTREL] != NULL) {
        assert(dyn_info[DT_PLTREL]->d_un.d_val == DT_REL);
    }
    if (dyn_info[DT_JMPREL] != NULL) {
        dyn_info[DT_JMPREL]->d_un.d_ptr += load_offs;
    }
}

static void do_relocation(Elf32_Addr load_addr, Elf32_Addr load_offs, const Elf32_Rel *rel)
{
    Elf32_Addr *target_addr = (Elf32_Addr *)(load_addr + rel->r_offset);
    if (ELF32_R_TYPE(rel->r_info) == R_386_RELATIVE) {
        *target_addr += load_offs;
        return;
    }
    if (ELF32_R_TYPE(rel->r_info) == R_386_NONE) {
        return;
    }
    assert(! "unexpected dynamic reloc type");
}

static void do_relocations(Elf32_Addr load_addr, Elf32_Addr load_offs, Elf32_Addr rel_addr, Elf32_Addr rel_size)
{
    const Elf32_Rel *rel_start = (const Elf32_Rel *)(rel_addr);
    const Elf32_Rel *rel_end   = (const Elf32_Rel *)(rel_addr + rel_size);

    for (const Elf32_Rel *rel = rel_start; rel < rel_end; rel++) {
        do_relocation(load_addr, load_offs, rel);
    }
}

//------------------------------------------------------------------------------
// Public Functions
//------------------------------------------------------------------------------

void reloc(void)
{
    static volatile Elf32_Addr last_load_addr = 0;

    Elf32_Dyn *dyn_info[DT_NUM];

    for (int i = 0; i < DT_NUM; i++) {
        dyn_info[i] = NULL;
    }

    Elf32_Addr load_addr = get_load_address();
    Elf32_Addr load_offs = load_addr - last_load_addr;
    if (load_addr == last_load_addr) {
        return;
    }
    last_load_addr = load_addr;

    Elf32_Dyn *dyn_section = (Elf32_Dyn *)(load_addr + get_dynamic_section_offset());
    get_dynamic_info(dyn_section, load_offs, dyn_info);

    do_relocations(load_addr, load_offs, dyn_info[DT_REL]->d_un.d_ptr, dyn_info[DT_RELSZ]->d_un.d_val);

    if (dyn_info[DT_PLTREL]->d_un.d_val == DT_REL) {
        do_relocations(load_addr, load_offs, dyn_info[DT_JMPREL]->d_un.d_ptr, dyn_info[DT_PLTRELSZ]->d_un.d_val);
    }
}