blob: f132ea49e07185aee09243c3d6584c6e3b45d3f1 [file] [log] [blame]
Juan Cespedesd914a202004-11-10 00:15:33 +01001# include "config.h"
Juan Cespedesd44c6b81998-09-25 14:48:42 +02002
Juan Cespedesd914a202004-11-10 00:15:33 +01003#include <endian.h>
Juan Cespedes96935a91997-08-09 23:45:39 +02004#include <errno.h>
Juan Cespedesd914a202004-11-10 00:15:33 +01005#include <error.h>
Juan Cespedes96935a91997-08-09 23:45:39 +02006#include <fcntl.h>
Juan Cespedesd914a202004-11-10 00:15:33 +01007#include <gelf.h>
8#include <stdint.h>
9#include <stdlib.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020010#include <string.h>
Juan Cespedes5e01f651998-03-08 22:31:44 +010011#include <unistd.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020012
Juan Cespedesf7281232009-06-25 16:11:21 +020013#include "common.h"
Juan Cespedes96935a91997-08-09 23:45:39 +020014
Ian Wienand2d45b1a2006-02-20 22:48:07 +010015static void do_init_elf(struct ltelf *lte, const char *filename);
16static void do_close_elf(struct ltelf *lte);
17static void add_library_symbol(GElf_Addr addr, const char *name,
18 struct library_symbol **library_symbolspp,
Paul Gilliam76c61f12006-06-14 06:55:21 +020019 enum toplt type_of_plt, int is_weak);
Ian Wienand2d45b1a2006-02-20 22:48:07 +010020static int in_load_libraries(const char *name, struct ltelf *lte);
Ian Wienand4bfcedd2006-08-07 04:03:15 +020021static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020022
Paul Gilliambe320772006-04-24 22:06:23 +020023#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010024extern char *PLTs_initialized_by_here;
Paul Gilliambe320772006-04-24 22:06:23 +020025#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010026
Juan Cespedesf1350522008-12-16 18:19:58 +010027static void
28do_init_elf(struct ltelf *lte, const char *filename) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +010029 int i;
30 GElf_Addr relplt_addr = 0;
31 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +020032
Juan Cespedescd8976d2009-05-14 13:47:58 +020033 debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +010034 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +020035
Ian Wienand2d45b1a2006-02-20 22:48:07 +010036 memset(lte, 0, sizeof(*lte));
37 lte->fd = open(filename, O_RDONLY);
38 if (lte->fd == -1)
39 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +020040
Juan Cespedesd914a202004-11-10 00:15:33 +010041#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010042 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020043#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +010044 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020045#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +020046
Ian Wienand2d45b1a2006-02-20 22:48:07 +010047 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
48 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020049
Ian Wienand2d45b1a2006-02-20 22:48:07 +010050 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
51 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
52 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020053
Ian Wienand2d45b1a2006-02-20 22:48:07 +010054 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
55 error(EXIT_FAILURE, 0,
56 "\"%s\" is not an ELF executable nor shared library",
57 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020058
Ian Wienand2d45b1a2006-02-20 22:48:07 +010059 if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
60 || lte->ehdr.e_machine != LT_ELF_MACHINE)
Juan Cespedesd914a202004-11-10 00:15:33 +010061#ifdef LT_ELF_MACHINE2
Ian Wienand2d45b1a2006-02-20 22:48:07 +010062 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
63 || lte->ehdr.e_machine != LT_ELF_MACHINE2)
Juan Cespedesd914a202004-11-10 00:15:33 +010064#endif
65#ifdef LT_ELF_MACHINE3
Ian Wienand2d45b1a2006-02-20 22:48:07 +010066 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3
67 || lte->ehdr.e_machine != LT_ELF_MACHINE3)
Juan Cespedesd914a202004-11-10 00:15:33 +010068#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +010069 )
70 error(EXIT_FAILURE, 0,
71 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010072
Ian Wienand2d45b1a2006-02-20 22:48:07 +010073 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
74 Elf_Scn *scn;
75 GElf_Shdr shdr;
76 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +010077
Ian Wienand2d45b1a2006-02-20 22:48:07 +010078 scn = elf_getscn(lte->elf, i);
79 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
80 error(EXIT_FAILURE, 0,
81 "Couldn't get section header from \"%s\"",
82 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010083
Ian Wienand2d45b1a2006-02-20 22:48:07 +010084 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
85 if (name == NULL)
86 error(EXIT_FAILURE, 0,
87 "Couldn't get section header from \"%s\"",
88 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010089
Ian Wienand2d45b1a2006-02-20 22:48:07 +010090 if (shdr.sh_type == SHT_SYMTAB) {
91 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +010092
Ian Wienand2d45b1a2006-02-20 22:48:07 +010093 lte->symtab = elf_getdata(scn, NULL);
94 lte->symtab_count = shdr.sh_size / shdr.sh_entsize;
95 if ((lte->symtab == NULL
96 || elf_getdata(scn, lte->symtab) != NULL)
97 && opt_x != NULL)
98 error(EXIT_FAILURE, 0,
99 "Couldn't get .symtab data from \"%s\"",
100 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100101
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100102 scn = elf_getscn(lte->elf, shdr.sh_link);
103 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
104 error(EXIT_FAILURE, 0,
105 "Couldn't get section header from \"%s\"",
106 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100107
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100108 data = elf_getdata(scn, NULL);
109 if (data == NULL || elf_getdata(scn, data) != NULL
110 || shdr.sh_size != data->d_size || data->d_off)
111 error(EXIT_FAILURE, 0,
112 "Couldn't get .strtab data from \"%s\"",
113 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100114
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100115 lte->strtab = data->d_buf;
116 } else if (shdr.sh_type == SHT_DYNSYM) {
117 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100118
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100119 lte->dynsym = elf_getdata(scn, NULL);
120 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
121 if (lte->dynsym == NULL
122 || elf_getdata(scn, lte->dynsym) != NULL)
123 error(EXIT_FAILURE, 0,
124 "Couldn't get .dynsym data from \"%s\"",
125 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100126
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100127 scn = elf_getscn(lte->elf, shdr.sh_link);
128 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
129 error(EXIT_FAILURE, 0,
130 "Couldn't get section header from \"%s\"",
131 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100132
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100133 data = elf_getdata(scn, NULL);
134 if (data == NULL || elf_getdata(scn, data) != NULL
135 || shdr.sh_size != data->d_size || data->d_off)
136 error(EXIT_FAILURE, 0,
137 "Couldn't get .dynstr data from \"%s\"",
138 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100139
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100140 lte->dynstr = data->d_buf;
141 } else if (shdr.sh_type == SHT_DYNAMIC) {
142 Elf_Data *data;
143 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100144
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100145 data = elf_getdata(scn, NULL);
146 if (data == NULL || elf_getdata(scn, data) != NULL)
147 error(EXIT_FAILURE, 0,
148 "Couldn't get .dynamic data from \"%s\"",
149 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100150
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100151 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
152 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100153
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100154 if (gelf_getdyn(data, j, &dyn) == NULL)
155 error(EXIT_FAILURE, 0,
156 "Couldn't get .dynamic data from \"%s\"",
157 filename);
Eric Vaitl1228a912006-12-28 16:16:56 +0100158#ifdef __mips__
159/**
160 MIPS ABI Supplement:
Ian Wienand9a2ad352006-02-20 22:44:45 +0100161
Juan Cespedesf1350522008-12-16 18:19:58 +0100162 DT_PLTGOT This member holds the address of the .got section.
Eric Vaitl1228a912006-12-28 16:16:56 +0100163
164 DT_MIPS_SYMTABNO This member holds the number of entries in the
165 .dynsym section.
166
167 DT_MIPS_LOCAL_GOTNO This member holds the number of local global
168 offset table entries.
169
170 DT_MIPS_GOTSYM This member holds the index of the first dyamic
171 symbol table entry that corresponds to an entry in the gobal offset
172 table.
173
174 */
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200175 if(dyn.d_tag==DT_PLTGOT){
Juan Cespedesf1350522008-12-16 18:19:58 +0100176 lte->pltgot_addr=dyn.d_un.d_ptr;
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200177 }
178 if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
179 lte->mips_local_gotno=dyn.d_un.d_val;
180 }
181 if(dyn.d_tag==DT_MIPS_GOTSYM){
182 lte->mips_gotsym=dyn.d_un.d_val;
183 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100184#endif // __mips__
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100185 if (dyn.d_tag == DT_JMPREL)
186 relplt_addr = dyn.d_un.d_ptr;
187 else if (dyn.d_tag == DT_PLTRELSZ)
188 relplt_size = dyn.d_un.d_val;
189 }
190 } else if (shdr.sh_type == SHT_HASH) {
191 Elf_Data *data;
192 size_t j;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100193
Petr Machata35fe5182006-07-18 12:58:12 +0200194 lte->hash_type = SHT_HASH;
195
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100196 data = elf_getdata(scn, NULL);
197 if (data == NULL || elf_getdata(scn, data) != NULL
198 || data->d_off || data->d_size != shdr.sh_size)
199 error(EXIT_FAILURE, 0,
200 "Couldn't get .hash data from \"%s\"",
201 filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100202
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100203 if (shdr.sh_entsize == 4) {
204 /* Standard conforming ELF. */
205 if (data->d_type != ELF_T_WORD)
206 error(EXIT_FAILURE, 0,
207 "Couldn't get .hash data from \"%s\"",
208 filename);
209 lte->hash = (Elf32_Word *) data->d_buf;
210 } else if (shdr.sh_entsize == 8) {
211 /* Alpha or s390x. */
212 Elf32_Word *dst, *src;
213 size_t hash_count = data->d_size / 8;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100214
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100215 lte->hash = (Elf32_Word *)
216 malloc(hash_count * sizeof(Elf32_Word));
217 if (lte->hash == NULL)
218 error(EXIT_FAILURE, 0,
219 "Couldn't convert .hash section from \"%s\"",
220 filename);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200221 lte->lte_flags |= LTE_HASH_MALLOCED;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100222 dst = lte->hash;
223 src = (Elf32_Word *) data->d_buf;
224 if ((data->d_type == ELF_T_WORD
225 && __BYTE_ORDER == __BIG_ENDIAN)
226 || (data->d_type == ELF_T_XWORD
227 && lte->ehdr.e_ident[EI_DATA] ==
228 ELFDATA2MSB))
229 ++src;
230 for (j = 0; j < hash_count; ++j, src += 2)
231 *dst++ = *src;
232 } else
233 error(EXIT_FAILURE, 0,
234 "Unknown .hash sh_entsize in \"%s\"",
235 filename);
Petr Machata35fe5182006-07-18 12:58:12 +0200236 } else if (shdr.sh_type == SHT_GNU_HASH
237 && lte->hash == NULL) {
238 Elf_Data *data;
Petr Machata35fe5182006-07-18 12:58:12 +0200239
240 lte->hash_type = SHT_GNU_HASH;
241
242 if (shdr.sh_entsize != 0
243 && shdr.sh_entsize != 4) {
244 error(EXIT_FAILURE, 0,
Olaf Hering03c087b2006-09-25 02:31:27 +0200245 ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu",
Petr Machata35fe5182006-07-18 12:58:12 +0200246 filename, shdr.sh_entsize);
247 }
248
249 data = elf_getdata(scn, NULL);
250 if (data == NULL || elf_getdata(scn, data) != NULL
251 || data->d_off || data->d_size != shdr.sh_size)
252 error(EXIT_FAILURE, 0,
253 "Couldn't get .gnu.hash data from \"%s\"",
254 filename);
255
256 lte->hash = (Elf32_Word *) data->d_buf;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100257 } else if (shdr.sh_type == SHT_PROGBITS
258 || shdr.sh_type == SHT_NOBITS) {
259 if (strcmp(name, ".plt") == 0) {
260 lte->plt_addr = shdr.sh_addr;
261 lte->plt_size = shdr.sh_size;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200262 if (shdr.sh_flags & SHF_EXECINSTR) {
263 lte->lte_flags |= LTE_PLT_EXECUTABLE;
264 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100265 }
266#ifdef ARCH_SUPPORTS_OPD
267 else if (strcmp(name, ".opd") == 0) {
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200268 lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100269 lte->opd_size = shdr.sh_size;
270 lte->opd = elf_rawdata(scn, NULL);
271 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100272#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100273 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100274 }
275
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100276 if (lte->dynsym == NULL || lte->dynstr == NULL)
277 error(EXIT_FAILURE, 0,
278 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100279
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100280 if (!relplt_addr || !lte->plt_addr) {
281 debug(1, "%s has no PLT relocations", filename);
282 lte->relplt = NULL;
283 lte->relplt_count = 0;
284 } else {
285 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
286 Elf_Scn *scn;
287 GElf_Shdr shdr;
288
289 scn = elf_getscn(lte->elf, i);
290 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
291 error(EXIT_FAILURE, 0,
292 "Couldn't get section header from \"%s\"",
293 filename);
294 if (shdr.sh_addr == relplt_addr
295 && shdr.sh_size == relplt_size) {
296 lte->relplt = elf_getdata(scn, NULL);
297 lte->relplt_count =
298 shdr.sh_size / shdr.sh_entsize;
299 if (lte->relplt == NULL
300 || elf_getdata(scn, lte->relplt) != NULL)
301 error(EXIT_FAILURE, 0,
302 "Couldn't get .rel*.plt data from \"%s\"",
303 filename);
304 break;
305 }
306 }
307
308 if (i == lte->ehdr.e_shnum)
309 error(EXIT_FAILURE, 0,
310 "Couldn't find .rel*.plt section in \"%s\"",
311 filename);
312
313 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
314 }
315}
316
Juan Cespedesf1350522008-12-16 18:19:58 +0100317static void
318do_close_elf(struct ltelf *lte) {
Juan Cespedescd8976d2009-05-14 13:47:58 +0200319 debug(DEBUG_FUNCTION, "do_close_elf()");
Paul Gilliam76c61f12006-06-14 06:55:21 +0200320 if (lte->lte_flags & LTE_HASH_MALLOCED)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100321 free((char *)lte->hash);
322 elf_end(lte->elf);
323 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200324}
325
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100326static void
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100327add_library_symbol(GElf_Addr addr, const char *name,
328 struct library_symbol **library_symbolspp,
Juan Cespedesf1350522008-12-16 18:19:58 +0100329 enum toplt type_of_plt, int is_weak) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100330 struct library_symbol *s;
Juan Cespedescd8976d2009-05-14 13:47:58 +0200331
332 debug(DEBUG_FUNCTION, "add_library_symbol()");
333
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100334 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
335 if (s == NULL)
336 error(EXIT_FAILURE, errno, "add_library_symbol failed");
337
338 s->needs_init = 1;
339 s->is_weak = is_weak;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200340 s->plt_type = type_of_plt;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100341 s->next = *library_symbolspp;
342 s->enter_addr = (void *)(uintptr_t) addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100343 s->name = (char *)(s + 1);
344 strcpy(s->name, name);
345 *library_symbolspp = s;
346
347 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100348}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200349
Olaf Hering03c087b2006-09-25 02:31:27 +0200350/* stolen from elfutils-0.123 */
Juan Cespedesf1350522008-12-16 18:19:58 +0100351static unsigned long
352private_elf_gnu_hash(const char *name) {
Olaf Hering03c087b2006-09-25 02:31:27 +0200353 unsigned long h = 5381;
354 const unsigned char *string = (const unsigned char *)name;
355 unsigned char c;
356 for (c = *string; c; c = *++string)
357 h = h * 33 + c;
358 return h & 0xffffffff;
359}
360
Juan Cespedesf1350522008-12-16 18:19:58 +0100361static int
362in_load_libraries(const char *name, struct ltelf *lte) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100363 size_t i;
364 unsigned long hash;
Petr Machata35fe5182006-07-18 12:58:12 +0200365 unsigned long gnu_hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100366
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100367 if (!library_num)
368 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100369
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200370 hash = elf_hash((const unsigned char *)name);
Petr Machata07bc4d12006-11-30 14:47:40 +0100371 gnu_hash = private_elf_gnu_hash(name);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100372 for (i = 1; i <= library_num; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100373 if (lte[i].hash == NULL)
374 continue;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200375
Petr Machata35fe5182006-07-18 12:58:12 +0200376 if (lte[i].hash_type == SHT_GNU_HASH) {
377 Elf32_Word * hashbase = lte[i].hash;
378 Elf32_Word nbuckets = *hashbase++;
379 Elf32_Word symbias = *hashbase++;
380 Elf32_Word bitmask_nwords = *hashbase++;
Petr Machata35fe5182006-07-18 12:58:12 +0200381 Elf32_Word * buckets;
382 Elf32_Word * chain_zero;
383 Elf32_Word bucket;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200384
Petr Machatab3f8fef2006-11-30 14:45:07 +0100385 // +1 for skipped `shift'
386 hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
Petr Machata35fe5182006-07-18 12:58:12 +0200387 buckets = hashbase;
388 hashbase += nbuckets;
389 chain_zero = hashbase - symbias;
390 bucket = buckets[gnu_hash % nbuckets];
Juan Cespedesd914a202004-11-10 00:15:33 +0100391
Petr Machata35fe5182006-07-18 12:58:12 +0200392 if (bucket != 0) {
393 const Elf32_Word *hasharr = &chain_zero[bucket];
394 do
395 if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
396 int symidx = hasharr - chain_zero;
397 GElf_Sym sym;
398
399 if (gelf_getsym(lte[i].dynsym, symidx, &sym) == NULL)
400 error(EXIT_FAILURE, 0,
401 "Couldn't get symbol from .dynsym");
402
403 if (sym.st_value != 0
404 && sym.st_shndx != SHN_UNDEF
405 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
406 return 1;
407 }
408 while ((*hasharr++ & 1u) == 0);
409 }
Olaf Hering03c087b2006-09-25 02:31:27 +0200410 } else {
Petr Machata35fe5182006-07-18 12:58:12 +0200411 Elf32_Word nbuckets, symndx;
412 Elf32_Word *buckets, *chain;
413 nbuckets = lte[i].hash[0];
414 buckets = &lte[i].hash[2];
415 chain = &lte[i].hash[2 + nbuckets];
416
417 for (symndx = buckets[hash % nbuckets];
418 symndx != STN_UNDEF; symndx = chain[symndx]) {
419 GElf_Sym sym;
420
421 if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
422 error(EXIT_FAILURE, 0,
423 "Couldn't get symbol from .dynsym");
424
425 if (sym.st_value != 0
426 && sym.st_shndx != SHN_UNDEF
427 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
428 return 1;
429 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100430 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200431 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100432 return 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100433}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200434
Juan Cespedesf1350522008-12-16 18:19:58 +0100435static GElf_Addr
436opd2addr(struct ltelf *lte, GElf_Addr addr) {
Petr Machatab3f8fef2006-11-30 14:45:07 +0100437#ifdef ARCH_SUPPORTS_OPD
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200438 unsigned long base, offset;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200439
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100440 if (!lte->opd)
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200441 return addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100442
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200443 base = (unsigned long)lte->opd->d_buf;
444 offset = (unsigned long)addr - (unsigned long)lte->opd_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100445 if (offset > lte->opd_size)
446 error(EXIT_FAILURE, 0, "static plt not in .opd");
447
Olaf Heringa841f652006-09-15 01:57:49 +0200448 return *(GElf_Addr*)(base + offset);
Petr Machatab3f8fef2006-11-30 14:45:07 +0100449#else //!ARCH_SUPPORTS_OPD
450 return addr;
451#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100452}
453
Juan Cespedesf1350522008-12-16 18:19:58 +0100454struct library_symbol *
Juan Cespedesa8909f72009-04-28 20:02:41 +0200455read_elf(Process *proc) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100456 struct library_symbol *library_symbols = NULL;
Juan Cespedes8d1b92b2009-07-03 10:39:34 +0200457 struct ltelf lte[MAX_LIBRARIES + 1];
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100458 size_t i;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100459 struct opt_x_t *xptr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100460 struct library_symbol **lib_tail = NULL;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100461 int exit_out = 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100462
Juan Cespedescd8976d2009-05-14 13:47:58 +0200463 debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename);
464
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100465 elf_version(EV_CURRENT);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100466
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100467 do_init_elf(lte, proc->filename);
468 proc->e_machine = lte->ehdr.e_machine;
469 for (i = 0; i < library_num; ++i)
470 do_init_elf(&lte[i + 1], library[i]);
Eric Vaitl1228a912006-12-28 16:16:56 +0100471#ifdef __mips__
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200472 // MIPS doesn't use the PLT and the GOT entries get changed
Juan Cespedesf1350522008-12-16 18:19:58 +0100473 // on startup.
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200474 proc->need_to_reinitialize_breakpoints = 1;
475 for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
476 GElf_Sym sym;
477 const char *name;
478 GElf_Addr addr = arch_plt_sym_val(lte, i, 0);
479 if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
480 error(EXIT_FAILURE, 0,
481 "Couldn't get relocation from \"%s\"",
Juan Cespedesf1350522008-12-16 18:19:58 +0100482 proc->filename);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200483 }
484 name=lte->dynstr+sym.st_name;
Arnaud Patard95623b22010-01-08 08:40:02 -0500485 if (in_load_libraries(name, lte)) {
486 if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
487 debug(2,"sym %s not a function",name);
488 continue;
489 }
490 add_library_symbol(addr, name, &library_symbols, 0,
491 ELF64_ST_BIND(sym.st_info) != 0);
492 if (!lib_tail)
493 lib_tail = &(library_symbols->next);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200494 }
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200495 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100496#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100497 for (i = 0; i < lte->relplt_count; ++i) {
498 GElf_Rel rel;
499 GElf_Rela rela;
500 GElf_Sym sym;
501 GElf_Addr addr;
502 void *ret;
503 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100504
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100505 if (lte->relplt->d_type == ELF_T_REL) {
506 ret = gelf_getrel(lte->relplt, i, &rel);
507 rela.r_offset = rel.r_offset;
508 rela.r_info = rel.r_info;
509 rela.r_addend = 0;
510 } else
511 ret = gelf_getrela(lte->relplt, i, &rela);
512
513 if (ret == NULL
514 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
515 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
516 &sym) == NULL)
517 error(EXIT_FAILURE, 0,
518 "Couldn't get relocation from \"%s\"",
519 proc->filename);
520
Paul Gilliambe320772006-04-24 22:06:23 +0200521#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100522 if (!sym.st_value && PLTs_initialized_by_here)
523 proc->need_to_reinitialize_breakpoints = 1;
Paul Gilliambe320772006-04-24 22:06:23 +0200524#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100525
526 name = lte->dynstr + sym.st_name;
527 if (in_load_libraries(name, lte)) {
528 addr = arch_plt_sym_val(lte, i, &rela);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200529 add_library_symbol(addr, name, &library_symbols,
530 (PLTS_ARE_EXECUTABLE(lte)
531 ? LS_TOPLT_EXEC : LS_TOPLT_POINT),
Petr Machata7003fee2006-07-18 13:06:26 +0200532 ELF64_ST_BIND(sym.st_info) == STB_WEAK);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100533 if (!lib_tail)
534 lib_tail = &(library_symbols->next);
535 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100536 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100537#endif // !__mips__
Paul Gilliambe320772006-04-24 22:06:23 +0200538#ifdef PLT_REINITALISATION_BP
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200539 struct opt_x_t *main_cheat;
540
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100541 if (proc->need_to_reinitialize_breakpoints) {
Paul Gilliambe320772006-04-24 22:06:23 +0200542 /* Add "PLTs_initialized_by_here" to opt_x list, if not
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200543 already there. */
Paul Gilliam2b2507a2006-04-07 18:32:07 +0200544 main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100545 if (main_cheat == NULL)
Petr Machata7003fee2006-07-18 13:06:26 +0200546 error(EXIT_FAILURE, 0, "Couldn't allocate memory");
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100547 main_cheat->next = opt_x;
Paul Gilliambe320772006-04-24 22:06:23 +0200548 main_cheat->found = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100549 main_cheat->name = PLTs_initialized_by_here;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100550
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100551 for (xptr = opt_x; xptr; xptr = xptr->next)
552 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
553 && main_cheat) {
554 free(main_cheat);
555 main_cheat = NULL;
556 break;
557 }
558 if (main_cheat)
559 opt_x = main_cheat;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100560 }
Paul Gilliambe320772006-04-24 22:06:23 +0200561#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100562
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100563 for (i = 0; i < lte->symtab_count; ++i) {
564 GElf_Sym sym;
565 GElf_Addr addr;
566 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100567
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100568 if (gelf_getsym(lte->symtab, i, &sym) == NULL)
569 error(EXIT_FAILURE, 0,
570 "Couldn't get symbol from \"%s\"",
571 proc->filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100572
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100573 name = lte->strtab + sym.st_name;
574 addr = sym.st_value;
575 if (!addr)
576 continue;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100577
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100578 for (xptr = opt_x; xptr; xptr = xptr->next)
579 if (xptr->name && strcmp(xptr->name, name) == 0) {
580 /* FIXME: Should be able to use &library_symbols as above. But
581 when you do, none of the real library symbols cause breaks. */
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200582 add_library_symbol(opd2addr(lte, addr),
Paul Gilliam76c61f12006-06-14 06:55:21 +0200583 name, lib_tail, LS_TOPLT_NONE, 0);
Paul Gilliam24e643a2006-03-13 18:43:13 +0100584 xptr->found = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100585 break;
586 }
587 }
588 for (xptr = opt_x; xptr; xptr = xptr->next)
Paul Gilliam24e643a2006-03-13 18:43:13 +0100589 if ( ! xptr->found) {
590 char *badthing = "WARNING";
Paul Gilliambe320772006-04-24 22:06:23 +0200591#ifdef PLT_REINITALISATION_BP
592 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
593 if (lte->ehdr.e_entry) {
594 add_library_symbol (
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200595 opd2addr (lte, lte->ehdr.e_entry),
Paul Gilliambe320772006-04-24 22:06:23 +0200596 PLTs_initialized_by_here,
597 lib_tail, 1, 0);
598 fprintf (stderr, "WARNING: Using e_ent"
599 "ry from elf header (%p) for "
600 "address of \"%s\"\n", (void*)
601 (long) lte->ehdr.e_entry,
602 PLTs_initialized_by_here);
603 continue;
604 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100605 badthing = "ERROR";
606 exit_out = 1;
607 }
Paul Gilliambe320772006-04-24 22:06:23 +0200608#endif
Paul Gilliam24e643a2006-03-13 18:43:13 +0100609 fprintf (stderr,
Paul Gilliambe320772006-04-24 22:06:23 +0200610 "%s: Couldn't find symbol \"%s\" in file \"%s"
611 "\"\n", badthing, xptr->name, proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100612 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100613 if (exit_out) {
614 exit (1);
615 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100616
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100617 for (i = 0; i < library_num + 1; ++i)
618 do_close_elf(&lte[i]);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100619
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100620 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200621}