blob: 004a307261d22a14ce283795794ef2535ff34e61 [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>
Petr Machatafe1c1712010-10-27 16:57:34 +020012#include <assert.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020013
Juan Cespedesf7281232009-06-25 16:11:21 +020014#include "common.h"
Juan Cespedes96935a91997-08-09 23:45:39 +020015
Joe Damato7a2bdf82010-11-08 15:47:41 -080016void do_init_elf(struct ltelf *lte, const char *filename);
17void do_close_elf(struct ltelf *lte);
18void add_library_symbol(GElf_Addr addr, const char *name,
19 struct library_symbol **library_symbolspp,
20 enum toplt type_of_plt, int is_weak);
21int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym);
Ian Wienand4bfcedd2006-08-07 04:03:15 +020022static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020023
Joe Damato7a2bdf82010-11-08 15:47:41 -080024struct library_symbol *library_symbols = NULL;
Paul Gilliambe320772006-04-24 22:06:23 +020025#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010026extern char *PLTs_initialized_by_here;
Paul Gilliambe320772006-04-24 22:06:23 +020027#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010028
Petr Machatafe1c1712010-10-27 16:57:34 +020029#ifndef DT_PPC_GOT
30# define DT_PPC_GOT (DT_LOPROC + 0)
31#endif
32
33#define PPC_PLT_STUB_SIZE 16
34
35static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
36{
37 Elf_Data *data = elf_getdata(scn, NULL);
38 if (data == NULL || elf_getdata(scn, data) != NULL
39 || data->d_off || data->d_size != shdr->sh_size)
40 return NULL;
41 return data;
42}
43
44static int inside(GElf_Addr addr, GElf_Shdr *shdr)
45{
46 return addr >= shdr->sh_addr
47 && addr < shdr->sh_addr + shdr->sh_size;
48}
49
50static int maybe_pick_section(GElf_Addr addr,
51 Elf_Scn *in_sec, GElf_Shdr *in_shdr,
52 Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
53{
54 if (inside (addr, in_shdr)) {
55 *tgt_sec = in_sec;
56 *tgt_shdr = *in_shdr;
57 return 1;
58 }
59 return 0;
60}
61
62static int get_section_covering(struct ltelf *lte, GElf_Addr addr,
63 Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
64{
65 int i;
66 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
67 Elf_Scn *scn;
68 GElf_Shdr shdr;
69
70 scn = elf_getscn(lte->elf, i);
71 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
72 debug(1, "Couldn't read section or header.");
73 return 0;
74 }
75
76 if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr))
77 return 1;
78 }
79
80 return 0;
81}
82
83static GElf_Addr read32be(Elf_Data *data, size_t offset)
84{
85 if (data->d_size < offset + 4) {
86 debug(1, "Not enough data to read 32bit value at offset %zd.",
87 offset);
88 return 0;
89 }
90
91 unsigned char const *buf = data->d_buf + offset;
92 return ((Elf32_Word)buf[0] << 24)
93 | ((Elf32_Word)buf[1] << 16)
94 | ((Elf32_Word)buf[2] << 8)
95 | ((Elf32_Word)buf[3]);
96}
97
98static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot,
99 Elf_Data *plt_data)
100{
101 Elf_Scn *ppcgot_sec = NULL;
102 GElf_Shdr ppcgot_shdr;
103 if (ppcgot != 0
104 && !get_section_covering(lte, ppcgot, &ppcgot_sec, &ppcgot_shdr))
105 // xxx should be the log out
106 fprintf(stderr,
107 "DT_PPC_GOT=%#lx, but no such section found.\n",
108 ppcgot);
109
110 if (ppcgot_sec != NULL) {
111 Elf_Data *data = loaddata(ppcgot_sec, &ppcgot_shdr);
112 if (data == NULL
113 || data->d_size < 8 )
114 debug(1, "Couldn't read GOT data.");
115 else {
116 // where PPCGOT begins in .got
117 size_t offset = ppcgot - ppcgot_shdr.sh_addr;
118 GElf_Addr glink_vma = read32be(data, offset + 4);
119 if (glink_vma != 0) {
120 debug(1, "PPC GOT glink_vma address: %#lx",
121 glink_vma);
122 return glink_vma;
123 }
124 }
125 }
126
127 if (plt_data != NULL) {
128 GElf_Addr glink_vma = read32be(plt_data, 0);
129 debug(1, ".plt glink_vma address: %#lx", glink_vma);
130 return glink_vma;
131 }
132
133 return 0;
134}
135
Joe Damato7a2bdf82010-11-08 15:47:41 -0800136void
Juan Cespedesf1350522008-12-16 18:19:58 +0100137do_init_elf(struct ltelf *lte, const char *filename) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100138 int i;
139 GElf_Addr relplt_addr = 0;
140 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200141
Juan Cespedescd8976d2009-05-14 13:47:58 +0200142 debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100143 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +0200144
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100145 memset(lte, 0, sizeof(*lte));
146 lte->fd = open(filename, O_RDONLY);
147 if (lte->fd == -1)
148 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +0200149
Juan Cespedesd914a202004-11-10 00:15:33 +0100150#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100151 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200152#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100153 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200154#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200155
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100156 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
157 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200158
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100159 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
160 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
161 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200162
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100163 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
164 error(EXIT_FAILURE, 0,
165 "\"%s\" is not an ELF executable nor shared library",
166 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200167
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100168 if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
169 || lte->ehdr.e_machine != LT_ELF_MACHINE)
Juan Cespedesd914a202004-11-10 00:15:33 +0100170#ifdef LT_ELF_MACHINE2
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100171 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
172 || lte->ehdr.e_machine != LT_ELF_MACHINE2)
Juan Cespedesd914a202004-11-10 00:15:33 +0100173#endif
174#ifdef LT_ELF_MACHINE3
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100175 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3
176 || lte->ehdr.e_machine != LT_ELF_MACHINE3)
Juan Cespedesd914a202004-11-10 00:15:33 +0100177#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100178 )
179 error(EXIT_FAILURE, 0,
180 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100181
Petr Machatafe1c1712010-10-27 16:57:34 +0200182 Elf_Data *plt_data = NULL;
183 GElf_Addr ppcgot = 0;
184
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100185 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
186 Elf_Scn *scn;
187 GElf_Shdr shdr;
188 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +0100189
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100190 scn = elf_getscn(lte->elf, i);
191 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
192 error(EXIT_FAILURE, 0,
193 "Couldn't get section header from \"%s\"",
194 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100195
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100196 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
197 if (name == NULL)
198 error(EXIT_FAILURE, 0,
199 "Couldn't get section header from \"%s\"",
200 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100201
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100202 if (shdr.sh_type == SHT_SYMTAB) {
203 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100204
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100205 lte->symtab = elf_getdata(scn, NULL);
206 lte->symtab_count = shdr.sh_size / shdr.sh_entsize;
207 if ((lte->symtab == NULL
208 || elf_getdata(scn, lte->symtab) != NULL)
209 && opt_x != NULL)
210 error(EXIT_FAILURE, 0,
211 "Couldn't get .symtab data from \"%s\"",
212 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100213
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100214 scn = elf_getscn(lte->elf, shdr.sh_link);
215 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
216 error(EXIT_FAILURE, 0,
217 "Couldn't get section header from \"%s\"",
218 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100219
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100220 data = elf_getdata(scn, NULL);
221 if (data == NULL || elf_getdata(scn, data) != NULL
222 || shdr.sh_size != data->d_size || data->d_off)
223 error(EXIT_FAILURE, 0,
224 "Couldn't get .strtab data from \"%s\"",
225 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100226
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100227 lte->strtab = data->d_buf;
228 } else if (shdr.sh_type == SHT_DYNSYM) {
229 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100230
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100231 lte->dynsym = elf_getdata(scn, NULL);
232 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
233 if (lte->dynsym == NULL
234 || elf_getdata(scn, lte->dynsym) != NULL)
235 error(EXIT_FAILURE, 0,
236 "Couldn't get .dynsym data from \"%s\"",
237 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100238
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100239 scn = elf_getscn(lte->elf, shdr.sh_link);
240 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
241 error(EXIT_FAILURE, 0,
242 "Couldn't get section header from \"%s\"",
243 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100244
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100245 data = elf_getdata(scn, NULL);
246 if (data == NULL || elf_getdata(scn, data) != NULL
247 || shdr.sh_size != data->d_size || data->d_off)
248 error(EXIT_FAILURE, 0,
249 "Couldn't get .dynstr data from \"%s\"",
250 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100251
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100252 lte->dynstr = data->d_buf;
253 } else if (shdr.sh_type == SHT_DYNAMIC) {
254 Elf_Data *data;
255 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100256
Joe Damato87f4f582010-11-08 15:47:36 -0800257 lte->dyn_addr = shdr.sh_addr;
258 lte->dyn_sz = shdr.sh_size;
259
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100260 data = elf_getdata(scn, NULL);
261 if (data == NULL || elf_getdata(scn, data) != NULL)
262 error(EXIT_FAILURE, 0,
263 "Couldn't get .dynamic data from \"%s\"",
264 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100265
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100266 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
267 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100268
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100269 if (gelf_getdyn(data, j, &dyn) == NULL)
270 error(EXIT_FAILURE, 0,
271 "Couldn't get .dynamic data from \"%s\"",
272 filename);
Eric Vaitl1228a912006-12-28 16:16:56 +0100273#ifdef __mips__
274/**
275 MIPS ABI Supplement:
Ian Wienand9a2ad352006-02-20 22:44:45 +0100276
Juan Cespedesf1350522008-12-16 18:19:58 +0100277 DT_PLTGOT This member holds the address of the .got section.
Eric Vaitl1228a912006-12-28 16:16:56 +0100278
279 DT_MIPS_SYMTABNO This member holds the number of entries in the
280 .dynsym section.
281
282 DT_MIPS_LOCAL_GOTNO This member holds the number of local global
283 offset table entries.
284
285 DT_MIPS_GOTSYM This member holds the index of the first dyamic
286 symbol table entry that corresponds to an entry in the gobal offset
287 table.
288
289 */
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200290 if(dyn.d_tag==DT_PLTGOT){
Juan Cespedesf1350522008-12-16 18:19:58 +0100291 lte->pltgot_addr=dyn.d_un.d_ptr;
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200292 }
293 if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
294 lte->mips_local_gotno=dyn.d_un.d_val;
295 }
296 if(dyn.d_tag==DT_MIPS_GOTSYM){
297 lte->mips_gotsym=dyn.d_un.d_val;
298 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100299#endif // __mips__
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100300 if (dyn.d_tag == DT_JMPREL)
301 relplt_addr = dyn.d_un.d_ptr;
302 else if (dyn.d_tag == DT_PLTRELSZ)
303 relplt_size = dyn.d_un.d_val;
Petr Machatafe1c1712010-10-27 16:57:34 +0200304 else if (dyn.d_tag == DT_PPC_GOT) {
305 ppcgot = dyn.d_un.d_val;
306 debug(1, "ppcgot %#lx", ppcgot);
307 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100308 }
309 } else if (shdr.sh_type == SHT_HASH) {
310 Elf_Data *data;
311 size_t j;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100312
Petr Machata35fe5182006-07-18 12:58:12 +0200313 lte->hash_type = SHT_HASH;
314
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100315 data = elf_getdata(scn, NULL);
316 if (data == NULL || elf_getdata(scn, data) != NULL
317 || data->d_off || data->d_size != shdr.sh_size)
318 error(EXIT_FAILURE, 0,
319 "Couldn't get .hash data from \"%s\"",
320 filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100321
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100322 if (shdr.sh_entsize == 4) {
323 /* Standard conforming ELF. */
324 if (data->d_type != ELF_T_WORD)
325 error(EXIT_FAILURE, 0,
326 "Couldn't get .hash data from \"%s\"",
327 filename);
328 lte->hash = (Elf32_Word *) data->d_buf;
329 } else if (shdr.sh_entsize == 8) {
330 /* Alpha or s390x. */
331 Elf32_Word *dst, *src;
332 size_t hash_count = data->d_size / 8;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100333
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100334 lte->hash = (Elf32_Word *)
335 malloc(hash_count * sizeof(Elf32_Word));
336 if (lte->hash == NULL)
337 error(EXIT_FAILURE, 0,
338 "Couldn't convert .hash section from \"%s\"",
339 filename);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200340 lte->lte_flags |= LTE_HASH_MALLOCED;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100341 dst = lte->hash;
342 src = (Elf32_Word *) data->d_buf;
343 if ((data->d_type == ELF_T_WORD
344 && __BYTE_ORDER == __BIG_ENDIAN)
345 || (data->d_type == ELF_T_XWORD
346 && lte->ehdr.e_ident[EI_DATA] ==
347 ELFDATA2MSB))
348 ++src;
349 for (j = 0; j < hash_count; ++j, src += 2)
350 *dst++ = *src;
351 } else
352 error(EXIT_FAILURE, 0,
353 "Unknown .hash sh_entsize in \"%s\"",
354 filename);
Petr Machata35fe5182006-07-18 12:58:12 +0200355 } else if (shdr.sh_type == SHT_GNU_HASH
356 && lte->hash == NULL) {
357 Elf_Data *data;
Petr Machata35fe5182006-07-18 12:58:12 +0200358
359 lte->hash_type = SHT_GNU_HASH;
360
361 if (shdr.sh_entsize != 0
362 && shdr.sh_entsize != 4) {
363 error(EXIT_FAILURE, 0,
Olaf Hering03c087b2006-09-25 02:31:27 +0200364 ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu",
Petr Machata35fe5182006-07-18 12:58:12 +0200365 filename, shdr.sh_entsize);
366 }
367
Petr Machatafe1c1712010-10-27 16:57:34 +0200368 data = loaddata(scn, &shdr);
369 if (data == NULL)
Petr Machata35fe5182006-07-18 12:58:12 +0200370 error(EXIT_FAILURE, 0,
371 "Couldn't get .gnu.hash data from \"%s\"",
372 filename);
373
374 lte->hash = (Elf32_Word *) data->d_buf;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100375 } else if (shdr.sh_type == SHT_PROGBITS
376 || shdr.sh_type == SHT_NOBITS) {
377 if (strcmp(name, ".plt") == 0) {
378 lte->plt_addr = shdr.sh_addr;
379 lte->plt_size = shdr.sh_size;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200380 if (shdr.sh_flags & SHF_EXECINSTR) {
381 lte->lte_flags |= LTE_PLT_EXECUTABLE;
382 }
Petr Machatafe1c1712010-10-27 16:57:34 +0200383 if (lte->ehdr.e_machine == EM_PPC) {
384 plt_data = loaddata(scn, &shdr);
385 if (plt_data == NULL)
386 fprintf(stderr,
387 "Can't load .plt data\n");
388 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100389 }
390#ifdef ARCH_SUPPORTS_OPD
391 else if (strcmp(name, ".opd") == 0) {
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200392 lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100393 lte->opd_size = shdr.sh_size;
394 lte->opd = elf_rawdata(scn, NULL);
395 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100396#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100397 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100398 }
399
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100400 if (lte->dynsym == NULL || lte->dynstr == NULL)
401 error(EXIT_FAILURE, 0,
402 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100403
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100404 if (!relplt_addr || !lte->plt_addr) {
405 debug(1, "%s has no PLT relocations", filename);
406 lte->relplt = NULL;
407 lte->relplt_count = 0;
Petr Machatafe1c1712010-10-27 16:57:34 +0200408 } else if (relplt_size == 0) {
409 debug(1, "%s has unknown PLT size", filename);
410 lte->relplt = NULL;
411 lte->relplt_count = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100412 } else {
Petr Machatafe1c1712010-10-27 16:57:34 +0200413 if (lte->ehdr.e_machine == EM_PPC) {
414 GElf_Addr glink_vma
415 = get_glink_vma(lte, ppcgot, plt_data);
416
417 assert (relplt_size % 12 == 0);
418 size_t count = relplt_size / 12; // size of RELA entry
419 lte->plt_stub_vma = glink_vma
420 - (GElf_Addr)count * PPC_PLT_STUB_SIZE;
421 debug(1, "stub_vma is %#lx", lte->plt_stub_vma);
422 }
423
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100424 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
425 Elf_Scn *scn;
426 GElf_Shdr shdr;
427
428 scn = elf_getscn(lte->elf, i);
429 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
430 error(EXIT_FAILURE, 0,
431 "Couldn't get section header from \"%s\"",
432 filename);
433 if (shdr.sh_addr == relplt_addr
434 && shdr.sh_size == relplt_size) {
435 lte->relplt = elf_getdata(scn, NULL);
436 lte->relplt_count =
437 shdr.sh_size / shdr.sh_entsize;
438 if (lte->relplt == NULL
439 || elf_getdata(scn, lte->relplt) != NULL)
440 error(EXIT_FAILURE, 0,
441 "Couldn't get .rel*.plt data from \"%s\"",
442 filename);
443 break;
444 }
445 }
446
447 if (i == lte->ehdr.e_shnum)
448 error(EXIT_FAILURE, 0,
449 "Couldn't find .rel*.plt section in \"%s\"",
450 filename);
451
452 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
453 }
454}
455
Joe Damato7a2bdf82010-11-08 15:47:41 -0800456void
Juan Cespedesf1350522008-12-16 18:19:58 +0100457do_close_elf(struct ltelf *lte) {
Juan Cespedescd8976d2009-05-14 13:47:58 +0200458 debug(DEBUG_FUNCTION, "do_close_elf()");
Paul Gilliam76c61f12006-06-14 06:55:21 +0200459 if (lte->lte_flags & LTE_HASH_MALLOCED)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100460 free((char *)lte->hash);
461 elf_end(lte->elf);
462 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200463}
464
Joe Damato7a2bdf82010-11-08 15:47:41 -0800465void
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100466add_library_symbol(GElf_Addr addr, const char *name,
467 struct library_symbol **library_symbolspp,
Juan Cespedesf1350522008-12-16 18:19:58 +0100468 enum toplt type_of_plt, int is_weak) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100469 struct library_symbol *s;
Juan Cespedescd8976d2009-05-14 13:47:58 +0200470
471 debug(DEBUG_FUNCTION, "add_library_symbol()");
472
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100473 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
474 if (s == NULL)
475 error(EXIT_FAILURE, errno, "add_library_symbol failed");
476
477 s->needs_init = 1;
478 s->is_weak = is_weak;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200479 s->plt_type = type_of_plt;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100480 s->next = *library_symbolspp;
481 s->enter_addr = (void *)(uintptr_t) addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100482 s->name = (char *)(s + 1);
483 strcpy(s->name, name);
484 *library_symbolspp = s;
485
486 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100487}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200488
Olaf Hering03c087b2006-09-25 02:31:27 +0200489/* stolen from elfutils-0.123 */
Juan Cespedesf1350522008-12-16 18:19:58 +0100490static unsigned long
491private_elf_gnu_hash(const char *name) {
Olaf Hering03c087b2006-09-25 02:31:27 +0200492 unsigned long h = 5381;
493 const unsigned char *string = (const unsigned char *)name;
494 unsigned char c;
495 for (c = *string; c; c = *++string)
496 h = h * 33 + c;
497 return h & 0xffffffff;
498}
499
Juan Cespedesf1350522008-12-16 18:19:58 +0100500static int
Joe Damato3268c5a2010-11-08 15:47:38 -0800501symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym,
502 size_t symidx, const char *name)
503{
504 GElf_Sym tmp_sym;
505 GElf_Sym *tmp;
506
507 tmp = (sym) ? (sym) : (&tmp_sym);
508
509 if (gelf_getsym(lte[lte_i].dynsym, symidx, tmp) == NULL)
510 error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym");
511 else {
512 tmp->st_value += lte[lte_i].base_addr;
513 debug(2, "symbol found: %s, %zd, %lx",
514 name, lte_i, tmp->st_value);
515 }
516 return tmp->st_value != 0
517 && tmp->st_shndx != SHN_UNDEF
518 && strcmp(name, lte[lte_i].dynstr + tmp->st_name) == 0;
519}
520
Joe Damato7a2bdf82010-11-08 15:47:41 -0800521int
Joe Damato3268c5a2010-11-08 15:47:38 -0800522in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100523 size_t i;
524 unsigned long hash;
Petr Machata35fe5182006-07-18 12:58:12 +0200525 unsigned long gnu_hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100526
Joe Damato3268c5a2010-11-08 15:47:38 -0800527 if (!count)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100528 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100529
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200530 hash = elf_hash((const unsigned char *)name);
Petr Machata07bc4d12006-11-30 14:47:40 +0100531 gnu_hash = private_elf_gnu_hash(name);
Joe Damato3268c5a2010-11-08 15:47:38 -0800532
533 for (i = 0; i < count; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100534 if (lte[i].hash == NULL)
535 continue;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200536
Petr Machata35fe5182006-07-18 12:58:12 +0200537 if (lte[i].hash_type == SHT_GNU_HASH) {
538 Elf32_Word * hashbase = lte[i].hash;
539 Elf32_Word nbuckets = *hashbase++;
540 Elf32_Word symbias = *hashbase++;
541 Elf32_Word bitmask_nwords = *hashbase++;
Petr Machata35fe5182006-07-18 12:58:12 +0200542 Elf32_Word * buckets;
543 Elf32_Word * chain_zero;
544 Elf32_Word bucket;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200545
Petr Machatab3f8fef2006-11-30 14:45:07 +0100546 // +1 for skipped `shift'
547 hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
Petr Machata35fe5182006-07-18 12:58:12 +0200548 buckets = hashbase;
549 hashbase += nbuckets;
550 chain_zero = hashbase - symbias;
551 bucket = buckets[gnu_hash % nbuckets];
Juan Cespedesd914a202004-11-10 00:15:33 +0100552
Petr Machata35fe5182006-07-18 12:58:12 +0200553 if (bucket != 0) {
554 const Elf32_Word *hasharr = &chain_zero[bucket];
555 do
556 if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
557 int symidx = hasharr - chain_zero;
Joe Damato3268c5a2010-11-08 15:47:38 -0800558 if (symbol_matches(lte, i,
559 sym, symidx,
560 name))
Petr Machata35fe5182006-07-18 12:58:12 +0200561 return 1;
562 }
563 while ((*hasharr++ & 1u) == 0);
564 }
Olaf Hering03c087b2006-09-25 02:31:27 +0200565 } else {
Petr Machata35fe5182006-07-18 12:58:12 +0200566 Elf32_Word nbuckets, symndx;
567 Elf32_Word *buckets, *chain;
568 nbuckets = lte[i].hash[0];
569 buckets = &lte[i].hash[2];
570 chain = &lte[i].hash[2 + nbuckets];
571
572 for (symndx = buckets[hash % nbuckets];
Joe Damato3268c5a2010-11-08 15:47:38 -0800573 symndx != STN_UNDEF; symndx = chain[symndx])
574 if (symbol_matches(lte, i, sym, symndx, name))
Petr Machata35fe5182006-07-18 12:58:12 +0200575 return 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100576 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200577 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100578 return 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100579}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200580
Juan Cespedesf1350522008-12-16 18:19:58 +0100581static GElf_Addr
582opd2addr(struct ltelf *lte, GElf_Addr addr) {
Petr Machatab3f8fef2006-11-30 14:45:07 +0100583#ifdef ARCH_SUPPORTS_OPD
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200584 unsigned long base, offset;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200585
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100586 if (!lte->opd)
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200587 return addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100588
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200589 base = (unsigned long)lte->opd->d_buf;
590 offset = (unsigned long)addr - (unsigned long)lte->opd_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100591 if (offset > lte->opd_size)
592 error(EXIT_FAILURE, 0, "static plt not in .opd");
593
Olaf Heringa841f652006-09-15 01:57:49 +0200594 return *(GElf_Addr*)(base + offset);
Petr Machatab3f8fef2006-11-30 14:45:07 +0100595#else //!ARCH_SUPPORTS_OPD
596 return addr;
597#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100598}
599
Juan Cespedesf1350522008-12-16 18:19:58 +0100600struct library_symbol *
Juan Cespedesa8909f72009-04-28 20:02:41 +0200601read_elf(Process *proc) {
Juan Cespedes8d1b92b2009-07-03 10:39:34 +0200602 struct ltelf lte[MAX_LIBRARIES + 1];
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100603 size_t i;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100604 struct opt_x_t *xptr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100605 struct library_symbol **lib_tail = NULL;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100606 int exit_out = 0;
Joe Damato3268c5a2010-11-08 15:47:38 -0800607 int count = 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100608
Juan Cespedescd8976d2009-05-14 13:47:58 +0200609 debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename);
610
Joe Damato7a2bdf82010-11-08 15:47:41 -0800611 library_symbols = NULL;
612 library_num = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100613 elf_version(EV_CURRENT);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100614
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100615 do_init_elf(lte, proc->filename);
616 proc->e_machine = lte->ehdr.e_machine;
617 for (i = 0; i < library_num; ++i)
618 do_init_elf(&lte[i + 1], library[i]);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700619
620 if (!options.no_plt) {
Eric Vaitl1228a912006-12-28 16:16:56 +0100621#ifdef __mips__
Joe Damatofa2aefc2010-10-30 19:56:50 -0700622 // MIPS doesn't use the PLT and the GOT entries get changed
623 // on startup.
624 proc->need_to_reinitialize_breakpoints = 1;
625 for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
626 GElf_Sym sym;
627 const char *name;
628 GElf_Addr addr = arch_plt_sym_val(lte, i, 0);
629 if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
630 error(EXIT_FAILURE, 0,
631 "Couldn't get relocation from \"%s\"",
632 proc->filename);
633 }
634 name=lte->dynstr+sym.st_name;
Arnaud Patard95623b22010-01-08 08:40:02 -0500635 if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
636 debug(2,"sym %s not a function",name);
637 continue;
638 }
639 add_library_symbol(addr, name, &library_symbols, 0,
640 ELF64_ST_BIND(sym.st_info) != 0);
641 if (!lib_tail)
642 lib_tail = &(library_symbols->next);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200643 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100644#else
Joe Damatofa2aefc2010-10-30 19:56:50 -0700645 for (i = 0; i < lte->relplt_count; ++i) {
646 GElf_Rel rel;
647 GElf_Rela rela;
648 GElf_Sym sym;
649 GElf_Addr addr;
650 void *ret;
651 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100652
Joe Damatofa2aefc2010-10-30 19:56:50 -0700653 if (lte->relplt->d_type == ELF_T_REL) {
654 ret = gelf_getrel(lte->relplt, i, &rel);
655 rela.r_offset = rel.r_offset;
656 rela.r_info = rel.r_info;
657 rela.r_addend = 0;
658 } else
659 ret = gelf_getrela(lte->relplt, i, &rela);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100660
Joe Damatofa2aefc2010-10-30 19:56:50 -0700661 if (ret == NULL
662 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
663 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
664 &sym) == NULL)
665 error(EXIT_FAILURE, 0,
666 "Couldn't get relocation from \"%s\"",
667 proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100668
Paul Gilliambe320772006-04-24 22:06:23 +0200669#ifdef PLT_REINITALISATION_BP
Joe Damatofa2aefc2010-10-30 19:56:50 -0700670 if (!sym.st_value && PLTs_initialized_by_here)
671 proc->need_to_reinitialize_breakpoints = 1;
Paul Gilliambe320772006-04-24 22:06:23 +0200672#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100673
Joe Damato3268c5a2010-11-08 15:47:38 -0800674 name = lte->dynstr + sym.st_name;
675 count = library_num ? library_num+1 : 0;
Petr Machatafe1c1712010-10-27 16:57:34 +0200676
Joe Damato3268c5a2010-11-08 15:47:38 -0800677 if (in_load_libraries(name, lte, count, NULL)) {
678 enum toplt pltt;
679 if (sym.st_value == 0 && lte->plt_stub_vma != 0) {
680 pltt = LS_TOPLT_EXEC;
681 addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i;
682 }
683 else {
684 pltt = PLTS_ARE_EXECUTABLE(lte)
685 ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
686 addr = arch_plt_sym_val(lte, i, &rela);
687 }
688
689 add_library_symbol(addr, name, &library_symbols, pltt,
690 ELF64_ST_BIND(sym.st_info) == STB_WEAK);
691 if (!lib_tail)
692 lib_tail = &(library_symbols->next);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700693 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100694 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100695#endif // !__mips__
Paul Gilliambe320772006-04-24 22:06:23 +0200696#ifdef PLT_REINITALISATION_BP
Joe Damatofa2aefc2010-10-30 19:56:50 -0700697 struct opt_x_t *main_cheat;
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200698
Joe Damatofa2aefc2010-10-30 19:56:50 -0700699 if (proc->need_to_reinitialize_breakpoints) {
700 /* Add "PLTs_initialized_by_here" to opt_x list, if not
701 already there. */
702 main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
703 if (main_cheat == NULL)
704 error(EXIT_FAILURE, 0, "Couldn't allocate memory");
705 main_cheat->next = opt_x;
706 main_cheat->found = 0;
707 main_cheat->name = PLTs_initialized_by_here;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100708
Joe Damatofa2aefc2010-10-30 19:56:50 -0700709 for (xptr = opt_x; xptr; xptr = xptr->next)
710 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
711 && main_cheat) {
712 free(main_cheat);
713 main_cheat = NULL;
714 break;
715 }
716 if (main_cheat)
717 opt_x = main_cheat;
718 }
Paul Gilliambe320772006-04-24 22:06:23 +0200719#endif
Joe Damatofa2aefc2010-10-30 19:56:50 -0700720 } else {
721 lib_tail = &library_symbols;
722 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100723
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100724 for (i = 0; i < lte->symtab_count; ++i) {
725 GElf_Sym sym;
726 GElf_Addr addr;
727 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100728
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100729 if (gelf_getsym(lte->symtab, i, &sym) == NULL)
730 error(EXIT_FAILURE, 0,
731 "Couldn't get symbol from \"%s\"",
732 proc->filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100733
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100734 name = lte->strtab + sym.st_name;
735 addr = sym.st_value;
736 if (!addr)
737 continue;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100738
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100739 for (xptr = opt_x; xptr; xptr = xptr->next)
740 if (xptr->name && strcmp(xptr->name, name) == 0) {
741 /* FIXME: Should be able to use &library_symbols as above. But
742 when you do, none of the real library symbols cause breaks. */
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200743 add_library_symbol(opd2addr(lte, addr),
Paul Gilliam76c61f12006-06-14 06:55:21 +0200744 name, lib_tail, LS_TOPLT_NONE, 0);
Paul Gilliam24e643a2006-03-13 18:43:13 +0100745 xptr->found = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100746 break;
747 }
748 }
Joe Damatoe2a8f572010-11-08 15:47:40 -0800749
750 int found_count = 0;
751
752 for (xptr = opt_x; xptr; xptr = xptr->next) {
753 if (xptr->found)
754 continue;
755
756 GElf_Sym sym;
757 GElf_Addr addr;
758 if (in_load_libraries(xptr->name, lte, library_num+1, &sym)) {
759 debug(2, "found symbol %s @ %lx, adding it.", xptr->name, sym.st_value);
760 addr = sym.st_value;
761 if (ELF32_ST_TYPE (sym.st_info) == STT_FUNC) {
762 add_library_symbol(addr, xptr->name, lib_tail, LS_TOPLT_NONE, 0);
763 xptr->found = 1;
764 found_count++;
765 }
766 }
767 if (found_count == opt_x_cnt){
768 debug(2, "done, found everything: %d\n", found_count);
769 break;
770 }
771 }
772
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100773 for (xptr = opt_x; xptr; xptr = xptr->next)
Paul Gilliam24e643a2006-03-13 18:43:13 +0100774 if ( ! xptr->found) {
775 char *badthing = "WARNING";
Paul Gilliambe320772006-04-24 22:06:23 +0200776#ifdef PLT_REINITALISATION_BP
777 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
778 if (lte->ehdr.e_entry) {
779 add_library_symbol (
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200780 opd2addr (lte, lte->ehdr.e_entry),
Paul Gilliambe320772006-04-24 22:06:23 +0200781 PLTs_initialized_by_here,
782 lib_tail, 1, 0);
783 fprintf (stderr, "WARNING: Using e_ent"
784 "ry from elf header (%p) for "
785 "address of \"%s\"\n", (void*)
786 (long) lte->ehdr.e_entry,
787 PLTs_initialized_by_here);
788 continue;
789 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100790 badthing = "ERROR";
791 exit_out = 1;
792 }
Paul Gilliambe320772006-04-24 22:06:23 +0200793#endif
Paul Gilliam24e643a2006-03-13 18:43:13 +0100794 fprintf (stderr,
Paul Gilliambe320772006-04-24 22:06:23 +0200795 "%s: Couldn't find symbol \"%s\" in file \"%s"
796 "\"\n", badthing, xptr->name, proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100797 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100798 if (exit_out) {
799 exit (1);
800 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100801
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100802 for (i = 0; i < library_num + 1; ++i)
803 do_close_elf(&lte[i]);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100804
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100805 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200806}