blob: 085b23142ba8d6d0936be8f6f38b43ee992e0242 [file] [log] [blame]
Martin Roth9b1b3352016-02-24 12:27:06 -08001/* reloc.c - MemTest-86 Version 3.3
2 *
3 * Released under version 2 of the Gnu Public License.
4 * By Eric Biederman
5 */
6
7#include "stddef.h"
8#include "stdint.h"
9#include "elf.h"
10
11#define __ELF_NATIVE_CLASS 32
12#define ELF_MACHINE_NO_RELA 1
13
14/* We use this macro to refer to ELF types independent of the native wordsize.
15 `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */
16
17#define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type)
18#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
19#define _ElfW_1(e,w,t) e##w##t
20/* We use this macro to refer to ELF types independent of the native wordsize.
21 `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */
22#define ELFW(type) _ElfW (ELF, __ELF_NATIVE_CLASS, type)
23
24#define assert(expr) ((void) 0)
25
26 /* This #define produces dynamic linking inline functions for
27 bootstrap relocation instead of general-purpose relocation. */
28#define RTLD_BOOTSTRAP
29
Martin Roth4dcd13d2016-02-24 13:53:07 -080030struct link_map
Martin Roth9b1b3352016-02-24 12:27:06 -080031{
32 ElfW(Addr) l_addr; /* Current load address */
33 ElfW(Addr) ll_addr; /* Last load address */
34 ElfW(Dyn) *l_ld;
35 /* Indexed pointers to dynamic section.
36 [0,DT_NUM) are indexed by the processor-independent tags.
37 [DT_NUM,DT_NUM+DT_PROCNUM) are indexed by the tag minus DT_LOPROC.
38 [DT_NUM+DT_PROCNUM,DT_NUM+DT_PROCNUM+DT_EXTRANUM) are indexed
39 by DT_EXTRATAGIDX(tagvalue) and
40 [DT_NUM+DT_PROCNUM,
41 DT_NUM+DT_PROCNUM+DT_EXTRANUM)
42 are indexed by DT_EXTRATAGIDX(tagvalue) (see <elf.h>). */
43
44 ElfW(Dyn) *l_info[DT_NUM + DT_PROCNUM + DT_EXTRANUM];
45};
46
47
48/* Return the link-time address of _DYNAMIC. Conveniently, this is the
49 first element of the GOT. This must be inlined in a function which
50 uses global data. */
51static inline Elf32_Addr __attribute__ ((unused))
52elf_machine_dynamic (void)
53{
54 register Elf32_Addr *got asm ("%ebx");
55 return *got;
56}
57
58/* Return the run-time load address of the shared object. */
59static inline Elf32_Addr __attribute__ ((unused))
60elf_machine_load_address (void)
61{
62 Elf32_Addr addr;
63 asm volatile ("leal _start@GOTOFF(%%ebx), %0\n"
64 : "=r" (addr) : : "cc");
65 return addr;
66}
67
68/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
69 MAP is the object containing the reloc. */
70static inline void
71elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
72 const Elf32_Sym *sym, Elf32_Addr *const reloc_addr)
73{
74 Elf32_Addr ls_addr, s_addr;
75 Elf32_Addr value;
76 if (ELF32_R_TYPE (reloc->r_info) == R_386_RELATIVE)
77 {
78 *reloc_addr += map->l_addr - map->ll_addr;
79 return;
80 }
81 if (ELF32_R_TYPE(reloc->r_info) == R_386_NONE) {
82 return;
83 }
84 value = sym->st_value;
85 /* Every section except the undefined section has a base of map->l_addr */
86 ls_addr = sym->st_shndx == SHN_UNDEF ? 0 : map->ll_addr;
87 s_addr = sym->st_shndx == SHN_UNDEF ? 0 : map->l_addr;
88
89 switch (ELF32_R_TYPE (reloc->r_info))
90 {
91 case R_386_COPY:
92 {
93 /* Roll memcpy by hand as we don't have function calls yet. */
94 unsigned char *dest, *src;
95 long i;
96 dest = (unsigned char *)reloc_addr;
97 src = (unsigned char *)(value + s_addr);
98 for(i = 0; i < sym->st_size; i++) {
99 dest[i] = src[i];
100 }
101 }
102 break;
103 case R_386_GLOB_DAT:
104 *reloc_addr = s_addr + value;
105 break;
106 case R_386_JMP_SLOT:
107 *reloc_addr = s_addr + value;
108 break;
109 case R_386_32:
110 if (map->ll_addr == 0) {
111 *reloc_addr += value;
112 }
113 *reloc_addr += s_addr - ls_addr;
114 break;
115 case R_386_PC32:
116 if (map->ll_addr == 0) {
117 *reloc_addr += value - reloc->r_offset;
118 }
119 *reloc_addr += (s_addr - map->l_addr) - (ls_addr - map->ll_addr);
120 break;
121 default:
122 assert (! "unexpected dynamic reloc type");
123 break;
124 }
125}
126
127/* Read the dynamic section at DYN and fill in INFO with indices DT_*. */
128
129static inline void __attribute__ ((unused))
130elf_get_dynamic_info(ElfW(Dyn) *dyn, ElfW(Addr) l_addr,
131 ElfW(Dyn) *info[DT_NUM + DT_PROCNUM + DT_EXTRANUM])
132{
133 if (! dyn)
134 return;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800135
Martin Roth9b1b3352016-02-24 12:27:06 -0800136 while (dyn->d_tag != DT_NULL)
137 {
138 if (dyn->d_tag < DT_NUM)
139 info[dyn->d_tag] = dyn;
140 else if (dyn->d_tag >= DT_LOPROC &&
141 dyn->d_tag < DT_LOPROC + DT_PROCNUM)
142 info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn;
143 else if ((Elf32_Word) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM)
144 info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_PROCNUM
145 ] = dyn;
146 else
147 assert (! "bad dynamic tag");
148 ++dyn;
149 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800150
151 if (info[DT_PLTGOT] != NULL)
Martin Roth9b1b3352016-02-24 12:27:06 -0800152 info[DT_PLTGOT]->d_un.d_ptr += l_addr;
153 if (info[DT_STRTAB] != NULL)
154 info[DT_STRTAB]->d_un.d_ptr += l_addr;
155 if (info[DT_SYMTAB] != NULL)
156 info[DT_SYMTAB]->d_un.d_ptr += l_addr;
157#if ! ELF_MACHINE_NO_RELA
158 if (info[DT_RELA] != NULL)
159 {
160 assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela)));
161 info[DT_RELA]->d_un.d_ptr += l_addr;
162 }
163#endif
164#if ! ELF_MACHINE_NO_REL
165 if (info[DT_REL] != NULL)
166 {
167 assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel)));
168 info[DT_REL]->d_un.d_ptr += l_addr;
169 }
170#endif
171 if (info[DT_PLTREL] != NULL)
172 {
173#if ELF_MACHINE_NO_RELA
174 assert (info[DT_PLTREL]->d_un.d_val == DT_REL);
175#elif ELF_MACHINE_NO_REL
176 assert (info[DT_PLTREL]->d_un.d_val == DT_RELA);
177#else
178 assert (info[DT_PLTREL]->d_un.d_val == DT_REL
179 || info[DT_PLTREL]->d_un.d_val == DT_RELA);
180#endif
181 }
182 if (info[DT_JMPREL] != NULL)
183 info[DT_JMPREL]->d_un.d_ptr += l_addr;
184}
185
186
187
188/* Perform the relocations in MAP on the running program image as specified
189 by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT
190 relocations; they should be set up to call _dl_runtime_resolve, rather
191 than fully resolved now. */
192
193static inline void
194elf_dynamic_do_rel (struct link_map *map,
195 ElfW(Addr) reladdr, ElfW(Addr) relsize)
196{
197 const ElfW(Rel) *r = (const void *) reladdr;
198 const ElfW(Rel) *end = (const void *) (reladdr + relsize);
199
200 const ElfW(Sym) *const symtab =
201 (const void *) map->l_info[DT_SYMTAB]->d_un.d_ptr;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800202
Martin Roth9b1b3352016-02-24 12:27:06 -0800203 for (; r < end; ++r) {
204 elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
205 (void *) (map->l_addr + r->r_offset));
206 }
207}
208
209
210void _dl_start(void)
211{
212 static Elf32_Addr last_load_address = 0;
213 struct link_map map;
214 size_t cnt;
215
216
217 /* Partly clean the `map' structure up. Don't use `memset'
218 since it might nor be built in or inlined and we cannot make function
219 calls at this point. */
220 for (cnt = 0; cnt < sizeof(map.l_info) / sizeof(map.l_info[0]); ++cnt) {
221 map.l_info[cnt] = 0;
222 }
223
224 /* Get the last load address */
225 map.ll_addr = last_load_address;
226
227 /* Figure out the run-time load address of the dynamic linker itself. */
228 last_load_address = map.l_addr = elf_machine_load_address();
Martin Roth4dcd13d2016-02-24 13:53:07 -0800229
Martin Roth9b1b3352016-02-24 12:27:06 -0800230 /* Read our own dynamic section and fill in the info array. */
231 map.l_ld = (void *)map.l_addr + elf_machine_dynamic();
232
233 elf_get_dynamic_info (map.l_ld, map.l_addr - map.ll_addr, map.l_info);
234
235 /* Relocate ourselves so we can do normal function calls and
Martin Roth4dcd13d2016-02-24 13:53:07 -0800236 * data access using the global offset table.
Martin Roth9b1b3352016-02-24 12:27:06 -0800237 */
238#if !ELF_MACHINE_NO_REL
Martin Roth4dcd13d2016-02-24 13:53:07 -0800239 elf_dynamic_do_rel(&map,
Martin Roth9b1b3352016-02-24 12:27:06 -0800240 map.l_info[DT_REL]->d_un.d_ptr,
241 map.l_info[DT_RELSZ]->d_un.d_val);
242 if (map.l_info[DT_PLTREL]->d_un.d_val == DT_REL) {
Martin Roth4dcd13d2016-02-24 13:53:07 -0800243 elf_dynamic_do_rel(&map,
Martin Roth9b1b3352016-02-24 12:27:06 -0800244 map.l_info[DT_JMPREL]->d_un.d_ptr,
245 map.l_info[DT_PLTRELSZ]->d_un.d_val);
246 }
247#endif
248
249#if !ELF_MACHINE_NO_RELA
Martin Roth4dcd13d2016-02-24 13:53:07 -0800250 elf_dynamic_do_rela(&map,
Martin Roth9b1b3352016-02-24 12:27:06 -0800251 map.l_info[DT_RELA]->d_un.d_ptr,
252 map.l_info[DT_RELASZ]->d_un.d_val);
253 if (map.l_info[DT_PLTREL]->d_un.d_val == DT_RELA) {
Martin Roth4dcd13d2016-02-24 13:53:07 -0800254 elf_dynamic_do_rela(&map,
Martin Roth9b1b3352016-02-24 12:27:06 -0800255 map.l_info[DT_JMPREL]->d_un.d_ptr,
256 map.l_info[DT_PLTRELSZ]->d_un.d_val);
257 }
258#endif
259
260 /* Now life is sane; we can call functions and access global data.
261 Set up to use the operating system facilities, and find out from
262 the operating system's program loader where to find the program
263 header table in core. Put the rest of _dl_start into a separate
264 function, that way the compiler cannot put accesses to the GOT
265 before ELF_DYNAMIC_RELOCATE. */
266 return;
267}