blob: 83d10f14bea3b96d4c822bc6e79c72b2720c8eaf [file] [log] [blame]
Juan Cespedesd44c6b81998-09-25 14:48:42 +02001#if HAVE_CONFIG_H
Juan Cespedesd914a202004-11-10 00:15:33 +01002# include "config.h"
Juan Cespedesd44c6b81998-09-25 14:48:42 +02003#endif
4
Juan Cespedesd914a202004-11-10 00:15:33 +01005#include <endian.h>
Juan Cespedes96935a91997-08-09 23:45:39 +02006#include <errno.h>
Juan Cespedesd914a202004-11-10 00:15:33 +01007#include <error.h>
Juan Cespedes96935a91997-08-09 23:45:39 +02008#include <fcntl.h>
Juan Cespedesd914a202004-11-10 00:15:33 +01009#include <gelf.h>
10#include <stdint.h>
11#include <stdlib.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020012#include <string.h>
Juan Cespedes5e01f651998-03-08 22:31:44 +010013#include <unistd.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020014
Juan Cespedesf7281232009-06-25 16:11:21 +020015#include "common.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010016#include "elf.h"
Juan Cespedescac15c32003-01-31 18:58:58 +010017#include "debug.h"
Ian Wienand9a2ad352006-02-20 22:44:45 +010018#include "options.h"
Juan Cespedes96935a91997-08-09 23:45:39 +020019
Ian Wienand2d45b1a2006-02-20 22:48:07 +010020static void do_init_elf(struct ltelf *lte, const char *filename);
21static void do_close_elf(struct ltelf *lte);
22static void add_library_symbol(GElf_Addr addr, const char *name,
23 struct library_symbol **library_symbolspp,
Paul Gilliam76c61f12006-06-14 06:55:21 +020024 enum toplt type_of_plt, int is_weak);
Ian Wienand2d45b1a2006-02-20 22:48:07 +010025static int in_load_libraries(const char *name, struct ltelf *lte);
Ian Wienand4bfcedd2006-08-07 04:03:15 +020026static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020027
Paul Gilliambe320772006-04-24 22:06:23 +020028#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010029extern char *PLTs_initialized_by_here;
Paul Gilliambe320772006-04-24 22:06:23 +020030#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010031
Juan Cespedesf1350522008-12-16 18:19:58 +010032static void
33do_init_elf(struct ltelf *lte, const char *filename) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +010034 int i;
35 GElf_Addr relplt_addr = 0;
36 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +020037
Juan Cespedescd8976d2009-05-14 13:47:58 +020038 debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +010039 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +020040
Ian Wienand2d45b1a2006-02-20 22:48:07 +010041 memset(lte, 0, sizeof(*lte));
42 lte->fd = open(filename, O_RDONLY);
43 if (lte->fd == -1)
44 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +020045
Juan Cespedesd914a202004-11-10 00:15:33 +010046#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010047 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020048#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +010049 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020050#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +020051
Ian Wienand2d45b1a2006-02-20 22:48:07 +010052 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
53 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020054
Ian Wienand2d45b1a2006-02-20 22:48:07 +010055 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
56 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
57 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020058
Ian Wienand2d45b1a2006-02-20 22:48:07 +010059 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
60 error(EXIT_FAILURE, 0,
61 "\"%s\" is not an ELF executable nor shared library",
62 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020063
Ian Wienand2d45b1a2006-02-20 22:48:07 +010064 if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
65 || lte->ehdr.e_machine != LT_ELF_MACHINE)
Juan Cespedesd914a202004-11-10 00:15:33 +010066#ifdef LT_ELF_MACHINE2
Ian Wienand2d45b1a2006-02-20 22:48:07 +010067 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
68 || lte->ehdr.e_machine != LT_ELF_MACHINE2)
Juan Cespedesd914a202004-11-10 00:15:33 +010069#endif
70#ifdef LT_ELF_MACHINE3
Ian Wienand2d45b1a2006-02-20 22:48:07 +010071 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3
72 || lte->ehdr.e_machine != LT_ELF_MACHINE3)
Juan Cespedesd914a202004-11-10 00:15:33 +010073#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +010074 )
75 error(EXIT_FAILURE, 0,
76 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010077
Ian Wienand2d45b1a2006-02-20 22:48:07 +010078 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
79 Elf_Scn *scn;
80 GElf_Shdr shdr;
81 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +010082
Ian Wienand2d45b1a2006-02-20 22:48:07 +010083 scn = elf_getscn(lte->elf, i);
84 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
85 error(EXIT_FAILURE, 0,
86 "Couldn't get section header from \"%s\"",
87 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010088
Ian Wienand2d45b1a2006-02-20 22:48:07 +010089 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
90 if (name == NULL)
91 error(EXIT_FAILURE, 0,
92 "Couldn't get section header from \"%s\"",
93 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010094
Ian Wienand2d45b1a2006-02-20 22:48:07 +010095 if (shdr.sh_type == SHT_SYMTAB) {
96 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +010097
Ian Wienand2d45b1a2006-02-20 22:48:07 +010098 lte->symtab = elf_getdata(scn, NULL);
99 lte->symtab_count = shdr.sh_size / shdr.sh_entsize;
100 if ((lte->symtab == NULL
101 || elf_getdata(scn, lte->symtab) != NULL)
102 && opt_x != NULL)
103 error(EXIT_FAILURE, 0,
104 "Couldn't get .symtab data from \"%s\"",
105 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100106
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100107 scn = elf_getscn(lte->elf, shdr.sh_link);
108 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
109 error(EXIT_FAILURE, 0,
110 "Couldn't get section header from \"%s\"",
111 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100112
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100113 data = elf_getdata(scn, NULL);
114 if (data == NULL || elf_getdata(scn, data) != NULL
115 || shdr.sh_size != data->d_size || data->d_off)
116 error(EXIT_FAILURE, 0,
117 "Couldn't get .strtab data from \"%s\"",
118 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100119
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100120 lte->strtab = data->d_buf;
121 } else if (shdr.sh_type == SHT_DYNSYM) {
122 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100123
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100124 lte->dynsym = elf_getdata(scn, NULL);
125 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
126 if (lte->dynsym == NULL
127 || elf_getdata(scn, lte->dynsym) != NULL)
128 error(EXIT_FAILURE, 0,
129 "Couldn't get .dynsym data from \"%s\"",
130 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100131
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100132 scn = elf_getscn(lte->elf, shdr.sh_link);
133 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
134 error(EXIT_FAILURE, 0,
135 "Couldn't get section header from \"%s\"",
136 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100137
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100138 data = elf_getdata(scn, NULL);
139 if (data == NULL || elf_getdata(scn, data) != NULL
140 || shdr.sh_size != data->d_size || data->d_off)
141 error(EXIT_FAILURE, 0,
142 "Couldn't get .dynstr data from \"%s\"",
143 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100144
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100145 lte->dynstr = data->d_buf;
146 } else if (shdr.sh_type == SHT_DYNAMIC) {
147 Elf_Data *data;
148 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100149
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100150 data = elf_getdata(scn, NULL);
151 if (data == NULL || elf_getdata(scn, data) != NULL)
152 error(EXIT_FAILURE, 0,
153 "Couldn't get .dynamic data from \"%s\"",
154 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100155
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100156 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
157 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100158
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100159 if (gelf_getdyn(data, j, &dyn) == NULL)
160 error(EXIT_FAILURE, 0,
161 "Couldn't get .dynamic data from \"%s\"",
162 filename);
Eric Vaitl1228a912006-12-28 16:16:56 +0100163#ifdef __mips__
164/**
165 MIPS ABI Supplement:
Ian Wienand9a2ad352006-02-20 22:44:45 +0100166
Juan Cespedesf1350522008-12-16 18:19:58 +0100167 DT_PLTGOT This member holds the address of the .got section.
Eric Vaitl1228a912006-12-28 16:16:56 +0100168
169 DT_MIPS_SYMTABNO This member holds the number of entries in the
170 .dynsym section.
171
172 DT_MIPS_LOCAL_GOTNO This member holds the number of local global
173 offset table entries.
174
175 DT_MIPS_GOTSYM This member holds the index of the first dyamic
176 symbol table entry that corresponds to an entry in the gobal offset
177 table.
178
179 */
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200180 if(dyn.d_tag==DT_PLTGOT){
Juan Cespedesf1350522008-12-16 18:19:58 +0100181 lte->pltgot_addr=dyn.d_un.d_ptr;
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200182 }
183 if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
184 lte->mips_local_gotno=dyn.d_un.d_val;
185 }
186 if(dyn.d_tag==DT_MIPS_GOTSYM){
187 lte->mips_gotsym=dyn.d_un.d_val;
188 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100189#endif // __mips__
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100190 if (dyn.d_tag == DT_JMPREL)
191 relplt_addr = dyn.d_un.d_ptr;
192 else if (dyn.d_tag == DT_PLTRELSZ)
193 relplt_size = dyn.d_un.d_val;
194 }
195 } else if (shdr.sh_type == SHT_HASH) {
196 Elf_Data *data;
197 size_t j;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100198
Petr Machata35fe5182006-07-18 12:58:12 +0200199 lte->hash_type = SHT_HASH;
200
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100201 data = elf_getdata(scn, NULL);
202 if (data == NULL || elf_getdata(scn, data) != NULL
203 || data->d_off || data->d_size != shdr.sh_size)
204 error(EXIT_FAILURE, 0,
205 "Couldn't get .hash data from \"%s\"",
206 filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100207
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100208 if (shdr.sh_entsize == 4) {
209 /* Standard conforming ELF. */
210 if (data->d_type != ELF_T_WORD)
211 error(EXIT_FAILURE, 0,
212 "Couldn't get .hash data from \"%s\"",
213 filename);
214 lte->hash = (Elf32_Word *) data->d_buf;
215 } else if (shdr.sh_entsize == 8) {
216 /* Alpha or s390x. */
217 Elf32_Word *dst, *src;
218 size_t hash_count = data->d_size / 8;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100219
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100220 lte->hash = (Elf32_Word *)
221 malloc(hash_count * sizeof(Elf32_Word));
222 if (lte->hash == NULL)
223 error(EXIT_FAILURE, 0,
224 "Couldn't convert .hash section from \"%s\"",
225 filename);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200226 lte->lte_flags |= LTE_HASH_MALLOCED;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100227 dst = lte->hash;
228 src = (Elf32_Word *) data->d_buf;
229 if ((data->d_type == ELF_T_WORD
230 && __BYTE_ORDER == __BIG_ENDIAN)
231 || (data->d_type == ELF_T_XWORD
232 && lte->ehdr.e_ident[EI_DATA] ==
233 ELFDATA2MSB))
234 ++src;
235 for (j = 0; j < hash_count; ++j, src += 2)
236 *dst++ = *src;
237 } else
238 error(EXIT_FAILURE, 0,
239 "Unknown .hash sh_entsize in \"%s\"",
240 filename);
Petr Machata35fe5182006-07-18 12:58:12 +0200241 } else if (shdr.sh_type == SHT_GNU_HASH
242 && lte->hash == NULL) {
243 Elf_Data *data;
Petr Machata35fe5182006-07-18 12:58:12 +0200244
245 lte->hash_type = SHT_GNU_HASH;
246
247 if (shdr.sh_entsize != 0
248 && shdr.sh_entsize != 4) {
249 error(EXIT_FAILURE, 0,
Olaf Hering03c087b2006-09-25 02:31:27 +0200250 ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu",
Petr Machata35fe5182006-07-18 12:58:12 +0200251 filename, shdr.sh_entsize);
252 }
253
254 data = elf_getdata(scn, NULL);
255 if (data == NULL || elf_getdata(scn, data) != NULL
256 || data->d_off || data->d_size != shdr.sh_size)
257 error(EXIT_FAILURE, 0,
258 "Couldn't get .gnu.hash data from \"%s\"",
259 filename);
260
261 lte->hash = (Elf32_Word *) data->d_buf;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100262 } else if (shdr.sh_type == SHT_PROGBITS
263 || shdr.sh_type == SHT_NOBITS) {
264 if (strcmp(name, ".plt") == 0) {
265 lte->plt_addr = shdr.sh_addr;
266 lte->plt_size = shdr.sh_size;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200267 if (shdr.sh_flags & SHF_EXECINSTR) {
268 lte->lte_flags |= LTE_PLT_EXECUTABLE;
269 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100270 }
271#ifdef ARCH_SUPPORTS_OPD
272 else if (strcmp(name, ".opd") == 0) {
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200273 lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100274 lte->opd_size = shdr.sh_size;
275 lte->opd = elf_rawdata(scn, NULL);
276 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100277#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100278 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100279 }
280
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100281 if (lte->dynsym == NULL || lte->dynstr == NULL)
282 error(EXIT_FAILURE, 0,
283 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100284
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100285 if (!relplt_addr || !lte->plt_addr) {
286 debug(1, "%s has no PLT relocations", filename);
287 lte->relplt = NULL;
288 lte->relplt_count = 0;
289 } else {
290 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
291 Elf_Scn *scn;
292 GElf_Shdr shdr;
293
294 scn = elf_getscn(lte->elf, i);
295 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
296 error(EXIT_FAILURE, 0,
297 "Couldn't get section header from \"%s\"",
298 filename);
299 if (shdr.sh_addr == relplt_addr
300 && shdr.sh_size == relplt_size) {
301 lte->relplt = elf_getdata(scn, NULL);
302 lte->relplt_count =
303 shdr.sh_size / shdr.sh_entsize;
304 if (lte->relplt == NULL
305 || elf_getdata(scn, lte->relplt) != NULL)
306 error(EXIT_FAILURE, 0,
307 "Couldn't get .rel*.plt data from \"%s\"",
308 filename);
309 break;
310 }
311 }
312
313 if (i == lte->ehdr.e_shnum)
314 error(EXIT_FAILURE, 0,
315 "Couldn't find .rel*.plt section in \"%s\"",
316 filename);
317
318 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
319 }
320}
321
Juan Cespedesf1350522008-12-16 18:19:58 +0100322static void
323do_close_elf(struct ltelf *lte) {
Juan Cespedescd8976d2009-05-14 13:47:58 +0200324 debug(DEBUG_FUNCTION, "do_close_elf()");
Paul Gilliam76c61f12006-06-14 06:55:21 +0200325 if (lte->lte_flags & LTE_HASH_MALLOCED)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100326 free((char *)lte->hash);
327 elf_end(lte->elf);
328 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200329}
330
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100331static void
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100332add_library_symbol(GElf_Addr addr, const char *name,
333 struct library_symbol **library_symbolspp,
Juan Cespedesf1350522008-12-16 18:19:58 +0100334 enum toplt type_of_plt, int is_weak) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100335 struct library_symbol *s;
Juan Cespedescd8976d2009-05-14 13:47:58 +0200336
337 debug(DEBUG_FUNCTION, "add_library_symbol()");
338
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100339 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
340 if (s == NULL)
341 error(EXIT_FAILURE, errno, "add_library_symbol failed");
342
343 s->needs_init = 1;
344 s->is_weak = is_weak;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200345 s->plt_type = type_of_plt;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100346 s->next = *library_symbolspp;
347 s->enter_addr = (void *)(uintptr_t) addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100348 s->name = (char *)(s + 1);
349 strcpy(s->name, name);
350 *library_symbolspp = s;
351
352 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100353}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200354
Olaf Hering03c087b2006-09-25 02:31:27 +0200355/* stolen from elfutils-0.123 */
Juan Cespedesf1350522008-12-16 18:19:58 +0100356static unsigned long
357private_elf_gnu_hash(const char *name) {
Olaf Hering03c087b2006-09-25 02:31:27 +0200358 unsigned long h = 5381;
359 const unsigned char *string = (const unsigned char *)name;
360 unsigned char c;
361 for (c = *string; c; c = *++string)
362 h = h * 33 + c;
363 return h & 0xffffffff;
364}
365
Juan Cespedesf1350522008-12-16 18:19:58 +0100366static int
367in_load_libraries(const char *name, struct ltelf *lte) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100368 size_t i;
369 unsigned long hash;
Petr Machata35fe5182006-07-18 12:58:12 +0200370 unsigned long gnu_hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100371
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100372 if (!library_num)
373 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100374
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200375 hash = elf_hash((const unsigned char *)name);
Petr Machata07bc4d12006-11-30 14:47:40 +0100376 gnu_hash = private_elf_gnu_hash(name);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100377 for (i = 1; i <= library_num; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100378 if (lte[i].hash == NULL)
379 continue;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200380
Petr Machata35fe5182006-07-18 12:58:12 +0200381 if (lte[i].hash_type == SHT_GNU_HASH) {
382 Elf32_Word * hashbase = lte[i].hash;
383 Elf32_Word nbuckets = *hashbase++;
384 Elf32_Word symbias = *hashbase++;
385 Elf32_Word bitmask_nwords = *hashbase++;
Petr Machata35fe5182006-07-18 12:58:12 +0200386 Elf32_Word * buckets;
387 Elf32_Word * chain_zero;
388 Elf32_Word bucket;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200389
Petr Machatab3f8fef2006-11-30 14:45:07 +0100390 // +1 for skipped `shift'
391 hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
Petr Machata35fe5182006-07-18 12:58:12 +0200392 buckets = hashbase;
393 hashbase += nbuckets;
394 chain_zero = hashbase - symbias;
395 bucket = buckets[gnu_hash % nbuckets];
Juan Cespedesd914a202004-11-10 00:15:33 +0100396
Petr Machata35fe5182006-07-18 12:58:12 +0200397 if (bucket != 0) {
398 const Elf32_Word *hasharr = &chain_zero[bucket];
399 do
400 if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
401 int symidx = hasharr - chain_zero;
402 GElf_Sym sym;
403
404 if (gelf_getsym(lte[i].dynsym, symidx, &sym) == NULL)
405 error(EXIT_FAILURE, 0,
406 "Couldn't get symbol from .dynsym");
407
408 if (sym.st_value != 0
409 && sym.st_shndx != SHN_UNDEF
410 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
411 return 1;
412 }
413 while ((*hasharr++ & 1u) == 0);
414 }
Olaf Hering03c087b2006-09-25 02:31:27 +0200415 } else {
Petr Machata35fe5182006-07-18 12:58:12 +0200416 Elf32_Word nbuckets, symndx;
417 Elf32_Word *buckets, *chain;
418 nbuckets = lte[i].hash[0];
419 buckets = &lte[i].hash[2];
420 chain = &lte[i].hash[2 + nbuckets];
421
422 for (symndx = buckets[hash % nbuckets];
423 symndx != STN_UNDEF; symndx = chain[symndx]) {
424 GElf_Sym sym;
425
426 if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
427 error(EXIT_FAILURE, 0,
428 "Couldn't get symbol from .dynsym");
429
430 if (sym.st_value != 0
431 && sym.st_shndx != SHN_UNDEF
432 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
433 return 1;
434 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100435 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200436 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100437 return 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100438}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200439
Juan Cespedesf1350522008-12-16 18:19:58 +0100440static GElf_Addr
441opd2addr(struct ltelf *lte, GElf_Addr addr) {
Petr Machatab3f8fef2006-11-30 14:45:07 +0100442#ifdef ARCH_SUPPORTS_OPD
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200443 unsigned long base, offset;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200444
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100445 if (!lte->opd)
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200446 return addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100447
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200448 base = (unsigned long)lte->opd->d_buf;
449 offset = (unsigned long)addr - (unsigned long)lte->opd_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100450 if (offset > lte->opd_size)
451 error(EXIT_FAILURE, 0, "static plt not in .opd");
452
Olaf Heringa841f652006-09-15 01:57:49 +0200453 return *(GElf_Addr*)(base + offset);
Petr Machatab3f8fef2006-11-30 14:45:07 +0100454#else //!ARCH_SUPPORTS_OPD
455 return addr;
456#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100457}
458
Juan Cespedesf1350522008-12-16 18:19:58 +0100459struct library_symbol *
Juan Cespedesa8909f72009-04-28 20:02:41 +0200460read_elf(Process *proc) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100461 struct library_symbol *library_symbols = NULL;
462 struct ltelf lte[MAX_LIBRARY + 1];
463 size_t i;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100464 struct opt_x_t *xptr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100465 struct library_symbol **lib_tail = NULL;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100466 int exit_out = 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100467
Juan Cespedescd8976d2009-05-14 13:47:58 +0200468 debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename);
469
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100470 elf_version(EV_CURRENT);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100471
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100472 do_init_elf(lte, proc->filename);
473 proc->e_machine = lte->ehdr.e_machine;
474 for (i = 0; i < library_num; ++i)
475 do_init_elf(&lte[i + 1], library[i]);
Eric Vaitl1228a912006-12-28 16:16:56 +0100476#ifdef __mips__
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200477 // MIPS doesn't use the PLT and the GOT entries get changed
Juan Cespedesf1350522008-12-16 18:19:58 +0100478 // on startup.
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200479 proc->need_to_reinitialize_breakpoints = 1;
480 for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
481 GElf_Sym sym;
482 const char *name;
483 GElf_Addr addr = arch_plt_sym_val(lte, i, 0);
484 if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
485 error(EXIT_FAILURE, 0,
486 "Couldn't get relocation from \"%s\"",
Juan Cespedesf1350522008-12-16 18:19:58 +0100487 proc->filename);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200488 }
489 name=lte->dynstr+sym.st_name;
490 if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
491 debug(2,"sym %s not a function",name);
492 continue;
493 }
494 add_library_symbol(addr, name, &library_symbols, 0,
495 ELF64_ST_BIND(sym.st_info) != 0);
496 if (!lib_tail)
497 lib_tail = &(library_symbols->next);
498 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100499#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100500 for (i = 0; i < lte->relplt_count; ++i) {
501 GElf_Rel rel;
502 GElf_Rela rela;
503 GElf_Sym sym;
504 GElf_Addr addr;
505 void *ret;
506 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100507
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100508 if (lte->relplt->d_type == ELF_T_REL) {
509 ret = gelf_getrel(lte->relplt, i, &rel);
510 rela.r_offset = rel.r_offset;
511 rela.r_info = rel.r_info;
512 rela.r_addend = 0;
513 } else
514 ret = gelf_getrela(lte->relplt, i, &rela);
515
516 if (ret == NULL
517 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
518 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
519 &sym) == NULL)
520 error(EXIT_FAILURE, 0,
521 "Couldn't get relocation from \"%s\"",
522 proc->filename);
523
Paul Gilliambe320772006-04-24 22:06:23 +0200524#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100525 if (!sym.st_value && PLTs_initialized_by_here)
526 proc->need_to_reinitialize_breakpoints = 1;
Paul Gilliambe320772006-04-24 22:06:23 +0200527#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100528
529 name = lte->dynstr + sym.st_name;
530 if (in_load_libraries(name, lte)) {
531 addr = arch_plt_sym_val(lte, i, &rela);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200532 add_library_symbol(addr, name, &library_symbols,
533 (PLTS_ARE_EXECUTABLE(lte)
534 ? LS_TOPLT_EXEC : LS_TOPLT_POINT),
Petr Machata7003fee2006-07-18 13:06:26 +0200535 ELF64_ST_BIND(sym.st_info) == STB_WEAK);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100536 if (!lib_tail)
537 lib_tail = &(library_symbols->next);
538 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100539 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100540#endif // !__mips__
Paul Gilliambe320772006-04-24 22:06:23 +0200541#ifdef PLT_REINITALISATION_BP
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200542 struct opt_x_t *main_cheat;
543
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100544 if (proc->need_to_reinitialize_breakpoints) {
Paul Gilliambe320772006-04-24 22:06:23 +0200545 /* Add "PLTs_initialized_by_here" to opt_x list, if not
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200546 already there. */
Paul Gilliam2b2507a2006-04-07 18:32:07 +0200547 main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100548 if (main_cheat == NULL)
Petr Machata7003fee2006-07-18 13:06:26 +0200549 error(EXIT_FAILURE, 0, "Couldn't allocate memory");
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100550 main_cheat->next = opt_x;
Paul Gilliambe320772006-04-24 22:06:23 +0200551 main_cheat->found = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100552 main_cheat->name = PLTs_initialized_by_here;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100553
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100554 for (xptr = opt_x; xptr; xptr = xptr->next)
555 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
556 && main_cheat) {
557 free(main_cheat);
558 main_cheat = NULL;
559 break;
560 }
561 if (main_cheat)
562 opt_x = main_cheat;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100563 }
Paul Gilliambe320772006-04-24 22:06:23 +0200564#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100565
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100566 for (i = 0; i < lte->symtab_count; ++i) {
567 GElf_Sym sym;
568 GElf_Addr addr;
569 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100570
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100571 if (gelf_getsym(lte->symtab, i, &sym) == NULL)
572 error(EXIT_FAILURE, 0,
573 "Couldn't get symbol from \"%s\"",
574 proc->filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100575
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100576 name = lte->strtab + sym.st_name;
577 addr = sym.st_value;
578 if (!addr)
579 continue;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100580
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100581 for (xptr = opt_x; xptr; xptr = xptr->next)
582 if (xptr->name && strcmp(xptr->name, name) == 0) {
583 /* FIXME: Should be able to use &library_symbols as above. But
584 when you do, none of the real library symbols cause breaks. */
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200585 add_library_symbol(opd2addr(lte, addr),
Paul Gilliam76c61f12006-06-14 06:55:21 +0200586 name, lib_tail, LS_TOPLT_NONE, 0);
Paul Gilliam24e643a2006-03-13 18:43:13 +0100587 xptr->found = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100588 break;
589 }
590 }
591 for (xptr = opt_x; xptr; xptr = xptr->next)
Paul Gilliam24e643a2006-03-13 18:43:13 +0100592 if ( ! xptr->found) {
593 char *badthing = "WARNING";
Paul Gilliambe320772006-04-24 22:06:23 +0200594#ifdef PLT_REINITALISATION_BP
595 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
596 if (lte->ehdr.e_entry) {
597 add_library_symbol (
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200598 opd2addr (lte, lte->ehdr.e_entry),
Paul Gilliambe320772006-04-24 22:06:23 +0200599 PLTs_initialized_by_here,
600 lib_tail, 1, 0);
601 fprintf (stderr, "WARNING: Using e_ent"
602 "ry from elf header (%p) for "
603 "address of \"%s\"\n", (void*)
604 (long) lte->ehdr.e_entry,
605 PLTs_initialized_by_here);
606 continue;
607 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100608 badthing = "ERROR";
609 exit_out = 1;
610 }
Paul Gilliambe320772006-04-24 22:06:23 +0200611#endif
Paul Gilliam24e643a2006-03-13 18:43:13 +0100612 fprintf (stderr,
Paul Gilliambe320772006-04-24 22:06:23 +0200613 "%s: Couldn't find symbol \"%s\" in file \"%s"
614 "\"\n", badthing, xptr->name, proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100615 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100616 if (exit_out) {
617 exit (1);
618 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100619
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100620 for (i = 0; i < library_num + 1; ++i)
621 do_close_elf(&lte[i]);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100622
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100623 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200624}