blob: 05a6a17e9e60ab2287c93530e76aaee766496d5c [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
Ian Wienand2d45b1a2006-02-20 22:48:07 +010016static void do_init_elf(struct ltelf *lte, const char *filename);
17static void do_close_elf(struct ltelf *lte);
18static void add_library_symbol(GElf_Addr addr, const char *name,
19 struct library_symbol **library_symbolspp,
Paul Gilliam76c61f12006-06-14 06:55:21 +020020 enum toplt type_of_plt, int is_weak);
Joe Damato3268c5a2010-11-08 15:47:38 -080021static int 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
Paul Gilliambe320772006-04-24 22:06:23 +020024#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010025extern char *PLTs_initialized_by_here;
Paul Gilliambe320772006-04-24 22:06:23 +020026#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010027
Petr Machatafe1c1712010-10-27 16:57:34 +020028#ifndef DT_PPC_GOT
29# define DT_PPC_GOT (DT_LOPROC + 0)
30#endif
31
32#define PPC_PLT_STUB_SIZE 16
33
34static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
35{
36 Elf_Data *data = elf_getdata(scn, NULL);
37 if (data == NULL || elf_getdata(scn, data) != NULL
38 || data->d_off || data->d_size != shdr->sh_size)
39 return NULL;
40 return data;
41}
42
43static int inside(GElf_Addr addr, GElf_Shdr *shdr)
44{
45 return addr >= shdr->sh_addr
46 && addr < shdr->sh_addr + shdr->sh_size;
47}
48
49static int maybe_pick_section(GElf_Addr addr,
50 Elf_Scn *in_sec, GElf_Shdr *in_shdr,
51 Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
52{
53 if (inside (addr, in_shdr)) {
54 *tgt_sec = in_sec;
55 *tgt_shdr = *in_shdr;
56 return 1;
57 }
58 return 0;
59}
60
61static int get_section_covering(struct ltelf *lte, GElf_Addr addr,
62 Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
63{
64 int i;
65 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
66 Elf_Scn *scn;
67 GElf_Shdr shdr;
68
69 scn = elf_getscn(lte->elf, i);
70 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
71 debug(1, "Couldn't read section or header.");
72 return 0;
73 }
74
75 if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr))
76 return 1;
77 }
78
79 return 0;
80}
81
82static GElf_Addr read32be(Elf_Data *data, size_t offset)
83{
84 if (data->d_size < offset + 4) {
85 debug(1, "Not enough data to read 32bit value at offset %zd.",
86 offset);
87 return 0;
88 }
89
90 unsigned char const *buf = data->d_buf + offset;
91 return ((Elf32_Word)buf[0] << 24)
92 | ((Elf32_Word)buf[1] << 16)
93 | ((Elf32_Word)buf[2] << 8)
94 | ((Elf32_Word)buf[3]);
95}
96
97static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot,
98 Elf_Data *plt_data)
99{
100 Elf_Scn *ppcgot_sec = NULL;
101 GElf_Shdr ppcgot_shdr;
102 if (ppcgot != 0
103 && !get_section_covering(lte, ppcgot, &ppcgot_sec, &ppcgot_shdr))
104 // xxx should be the log out
105 fprintf(stderr,
106 "DT_PPC_GOT=%#lx, but no such section found.\n",
107 ppcgot);
108
109 if (ppcgot_sec != NULL) {
110 Elf_Data *data = loaddata(ppcgot_sec, &ppcgot_shdr);
111 if (data == NULL
112 || data->d_size < 8 )
113 debug(1, "Couldn't read GOT data.");
114 else {
115 // where PPCGOT begins in .got
116 size_t offset = ppcgot - ppcgot_shdr.sh_addr;
117 GElf_Addr glink_vma = read32be(data, offset + 4);
118 if (glink_vma != 0) {
119 debug(1, "PPC GOT glink_vma address: %#lx",
120 glink_vma);
121 return glink_vma;
122 }
123 }
124 }
125
126 if (plt_data != NULL) {
127 GElf_Addr glink_vma = read32be(plt_data, 0);
128 debug(1, ".plt glink_vma address: %#lx", glink_vma);
129 return glink_vma;
130 }
131
132 return 0;
133}
134
Juan Cespedesf1350522008-12-16 18:19:58 +0100135static void
136do_init_elf(struct ltelf *lte, const char *filename) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100137 int i;
138 GElf_Addr relplt_addr = 0;
139 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200140
Juan Cespedescd8976d2009-05-14 13:47:58 +0200141 debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100142 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +0200143
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100144 memset(lte, 0, sizeof(*lte));
145 lte->fd = open(filename, O_RDONLY);
146 if (lte->fd == -1)
147 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +0200148
Juan Cespedesd914a202004-11-10 00:15:33 +0100149#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100150 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200151#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100152 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200153#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200154
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100155 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
156 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200157
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100158 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
159 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
160 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200161
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100162 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
163 error(EXIT_FAILURE, 0,
164 "\"%s\" is not an ELF executable nor shared library",
165 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200166
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100167 if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
168 || lte->ehdr.e_machine != LT_ELF_MACHINE)
Juan Cespedesd914a202004-11-10 00:15:33 +0100169#ifdef LT_ELF_MACHINE2
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100170 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
171 || lte->ehdr.e_machine != LT_ELF_MACHINE2)
Juan Cespedesd914a202004-11-10 00:15:33 +0100172#endif
173#ifdef LT_ELF_MACHINE3
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100174 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3
175 || lte->ehdr.e_machine != LT_ELF_MACHINE3)
Juan Cespedesd914a202004-11-10 00:15:33 +0100176#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100177 )
178 error(EXIT_FAILURE, 0,
179 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100180
Petr Machatafe1c1712010-10-27 16:57:34 +0200181 Elf_Data *plt_data = NULL;
182 GElf_Addr ppcgot = 0;
183
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100184 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
185 Elf_Scn *scn;
186 GElf_Shdr shdr;
187 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +0100188
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100189 scn = elf_getscn(lte->elf, i);
190 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
191 error(EXIT_FAILURE, 0,
192 "Couldn't get section header from \"%s\"",
193 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100194
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100195 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
196 if (name == NULL)
197 error(EXIT_FAILURE, 0,
198 "Couldn't get section header from \"%s\"",
199 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100200
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100201 if (shdr.sh_type == SHT_SYMTAB) {
202 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100203
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100204 lte->symtab = elf_getdata(scn, NULL);
205 lte->symtab_count = shdr.sh_size / shdr.sh_entsize;
206 if ((lte->symtab == NULL
207 || elf_getdata(scn, lte->symtab) != NULL)
208 && opt_x != NULL)
209 error(EXIT_FAILURE, 0,
210 "Couldn't get .symtab data from \"%s\"",
211 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100212
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100213 scn = elf_getscn(lte->elf, shdr.sh_link);
214 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
215 error(EXIT_FAILURE, 0,
216 "Couldn't get section header from \"%s\"",
217 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100218
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100219 data = elf_getdata(scn, NULL);
220 if (data == NULL || elf_getdata(scn, data) != NULL
221 || shdr.sh_size != data->d_size || data->d_off)
222 error(EXIT_FAILURE, 0,
223 "Couldn't get .strtab data from \"%s\"",
224 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100225
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100226 lte->strtab = data->d_buf;
227 } else if (shdr.sh_type == SHT_DYNSYM) {
228 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100229
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100230 lte->dynsym = elf_getdata(scn, NULL);
231 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
232 if (lte->dynsym == NULL
233 || elf_getdata(scn, lte->dynsym) != NULL)
234 error(EXIT_FAILURE, 0,
235 "Couldn't get .dynsym data from \"%s\"",
236 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100237
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100238 scn = elf_getscn(lte->elf, shdr.sh_link);
239 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
240 error(EXIT_FAILURE, 0,
241 "Couldn't get section header from \"%s\"",
242 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100243
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100244 data = elf_getdata(scn, NULL);
245 if (data == NULL || elf_getdata(scn, data) != NULL
246 || shdr.sh_size != data->d_size || data->d_off)
247 error(EXIT_FAILURE, 0,
248 "Couldn't get .dynstr data from \"%s\"",
249 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100250
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100251 lte->dynstr = data->d_buf;
252 } else if (shdr.sh_type == SHT_DYNAMIC) {
253 Elf_Data *data;
254 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100255
Joe Damato87f4f582010-11-08 15:47:36 -0800256 lte->dyn_addr = shdr.sh_addr;
257 lte->dyn_sz = shdr.sh_size;
258
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100259 data = elf_getdata(scn, NULL);
260 if (data == NULL || elf_getdata(scn, data) != NULL)
261 error(EXIT_FAILURE, 0,
262 "Couldn't get .dynamic data from \"%s\"",
263 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100264
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100265 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
266 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100267
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100268 if (gelf_getdyn(data, j, &dyn) == NULL)
269 error(EXIT_FAILURE, 0,
270 "Couldn't get .dynamic data from \"%s\"",
271 filename);
Eric Vaitl1228a912006-12-28 16:16:56 +0100272#ifdef __mips__
273/**
274 MIPS ABI Supplement:
Ian Wienand9a2ad352006-02-20 22:44:45 +0100275
Juan Cespedesf1350522008-12-16 18:19:58 +0100276 DT_PLTGOT This member holds the address of the .got section.
Eric Vaitl1228a912006-12-28 16:16:56 +0100277
278 DT_MIPS_SYMTABNO This member holds the number of entries in the
279 .dynsym section.
280
281 DT_MIPS_LOCAL_GOTNO This member holds the number of local global
282 offset table entries.
283
284 DT_MIPS_GOTSYM This member holds the index of the first dyamic
285 symbol table entry that corresponds to an entry in the gobal offset
286 table.
287
288 */
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200289 if(dyn.d_tag==DT_PLTGOT){
Juan Cespedesf1350522008-12-16 18:19:58 +0100290 lte->pltgot_addr=dyn.d_un.d_ptr;
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200291 }
292 if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
293 lte->mips_local_gotno=dyn.d_un.d_val;
294 }
295 if(dyn.d_tag==DT_MIPS_GOTSYM){
296 lte->mips_gotsym=dyn.d_un.d_val;
297 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100298#endif // __mips__
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100299 if (dyn.d_tag == DT_JMPREL)
300 relplt_addr = dyn.d_un.d_ptr;
301 else if (dyn.d_tag == DT_PLTRELSZ)
302 relplt_size = dyn.d_un.d_val;
Petr Machatafe1c1712010-10-27 16:57:34 +0200303 else if (dyn.d_tag == DT_PPC_GOT) {
304 ppcgot = dyn.d_un.d_val;
305 debug(1, "ppcgot %#lx", ppcgot);
306 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100307 }
308 } else if (shdr.sh_type == SHT_HASH) {
309 Elf_Data *data;
310 size_t j;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100311
Petr Machata35fe5182006-07-18 12:58:12 +0200312 lte->hash_type = SHT_HASH;
313
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100314 data = elf_getdata(scn, NULL);
315 if (data == NULL || elf_getdata(scn, data) != NULL
316 || data->d_off || data->d_size != shdr.sh_size)
317 error(EXIT_FAILURE, 0,
318 "Couldn't get .hash data from \"%s\"",
319 filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100320
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100321 if (shdr.sh_entsize == 4) {
322 /* Standard conforming ELF. */
323 if (data->d_type != ELF_T_WORD)
324 error(EXIT_FAILURE, 0,
325 "Couldn't get .hash data from \"%s\"",
326 filename);
327 lte->hash = (Elf32_Word *) data->d_buf;
328 } else if (shdr.sh_entsize == 8) {
329 /* Alpha or s390x. */
330 Elf32_Word *dst, *src;
331 size_t hash_count = data->d_size / 8;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100332
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100333 lte->hash = (Elf32_Word *)
334 malloc(hash_count * sizeof(Elf32_Word));
335 if (lte->hash == NULL)
336 error(EXIT_FAILURE, 0,
337 "Couldn't convert .hash section from \"%s\"",
338 filename);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200339 lte->lte_flags |= LTE_HASH_MALLOCED;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100340 dst = lte->hash;
341 src = (Elf32_Word *) data->d_buf;
342 if ((data->d_type == ELF_T_WORD
343 && __BYTE_ORDER == __BIG_ENDIAN)
344 || (data->d_type == ELF_T_XWORD
345 && lte->ehdr.e_ident[EI_DATA] ==
346 ELFDATA2MSB))
347 ++src;
348 for (j = 0; j < hash_count; ++j, src += 2)
349 *dst++ = *src;
350 } else
351 error(EXIT_FAILURE, 0,
352 "Unknown .hash sh_entsize in \"%s\"",
353 filename);
Petr Machata35fe5182006-07-18 12:58:12 +0200354 } else if (shdr.sh_type == SHT_GNU_HASH
355 && lte->hash == NULL) {
356 Elf_Data *data;
Petr Machata35fe5182006-07-18 12:58:12 +0200357
358 lte->hash_type = SHT_GNU_HASH;
359
360 if (shdr.sh_entsize != 0
361 && shdr.sh_entsize != 4) {
362 error(EXIT_FAILURE, 0,
Olaf Hering03c087b2006-09-25 02:31:27 +0200363 ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu",
Petr Machata35fe5182006-07-18 12:58:12 +0200364 filename, shdr.sh_entsize);
365 }
366
Petr Machatafe1c1712010-10-27 16:57:34 +0200367 data = loaddata(scn, &shdr);
368 if (data == NULL)
Petr Machata35fe5182006-07-18 12:58:12 +0200369 error(EXIT_FAILURE, 0,
370 "Couldn't get .gnu.hash data from \"%s\"",
371 filename);
372
373 lte->hash = (Elf32_Word *) data->d_buf;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100374 } else if (shdr.sh_type == SHT_PROGBITS
375 || shdr.sh_type == SHT_NOBITS) {
376 if (strcmp(name, ".plt") == 0) {
377 lte->plt_addr = shdr.sh_addr;
378 lte->plt_size = shdr.sh_size;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200379 if (shdr.sh_flags & SHF_EXECINSTR) {
380 lte->lte_flags |= LTE_PLT_EXECUTABLE;
381 }
Petr Machatafe1c1712010-10-27 16:57:34 +0200382 if (lte->ehdr.e_machine == EM_PPC) {
383 plt_data = loaddata(scn, &shdr);
384 if (plt_data == NULL)
385 fprintf(stderr,
386 "Can't load .plt data\n");
387 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100388 }
389#ifdef ARCH_SUPPORTS_OPD
390 else if (strcmp(name, ".opd") == 0) {
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200391 lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100392 lte->opd_size = shdr.sh_size;
393 lte->opd = elf_rawdata(scn, NULL);
394 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100395#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100396 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100397 }
398
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100399 if (lte->dynsym == NULL || lte->dynstr == NULL)
400 error(EXIT_FAILURE, 0,
401 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100402
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100403 if (!relplt_addr || !lte->plt_addr) {
404 debug(1, "%s has no PLT relocations", filename);
405 lte->relplt = NULL;
406 lte->relplt_count = 0;
Petr Machatafe1c1712010-10-27 16:57:34 +0200407 } else if (relplt_size == 0) {
408 debug(1, "%s has unknown PLT size", filename);
409 lte->relplt = NULL;
410 lte->relplt_count = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100411 } else {
Petr Machatafe1c1712010-10-27 16:57:34 +0200412 if (lte->ehdr.e_machine == EM_PPC) {
413 GElf_Addr glink_vma
414 = get_glink_vma(lte, ppcgot, plt_data);
415
416 assert (relplt_size % 12 == 0);
417 size_t count = relplt_size / 12; // size of RELA entry
418 lte->plt_stub_vma = glink_vma
419 - (GElf_Addr)count * PPC_PLT_STUB_SIZE;
420 debug(1, "stub_vma is %#lx", lte->plt_stub_vma);
421 }
422
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100423 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
424 Elf_Scn *scn;
425 GElf_Shdr shdr;
426
427 scn = elf_getscn(lte->elf, i);
428 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
429 error(EXIT_FAILURE, 0,
430 "Couldn't get section header from \"%s\"",
431 filename);
432 if (shdr.sh_addr == relplt_addr
433 && shdr.sh_size == relplt_size) {
434 lte->relplt = elf_getdata(scn, NULL);
435 lte->relplt_count =
436 shdr.sh_size / shdr.sh_entsize;
437 if (lte->relplt == NULL
438 || elf_getdata(scn, lte->relplt) != NULL)
439 error(EXIT_FAILURE, 0,
440 "Couldn't get .rel*.plt data from \"%s\"",
441 filename);
442 break;
443 }
444 }
445
446 if (i == lte->ehdr.e_shnum)
447 error(EXIT_FAILURE, 0,
448 "Couldn't find .rel*.plt section in \"%s\"",
449 filename);
450
451 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
452 }
453}
454
Juan Cespedesf1350522008-12-16 18:19:58 +0100455static void
456do_close_elf(struct ltelf *lte) {
Juan Cespedescd8976d2009-05-14 13:47:58 +0200457 debug(DEBUG_FUNCTION, "do_close_elf()");
Paul Gilliam76c61f12006-06-14 06:55:21 +0200458 if (lte->lte_flags & LTE_HASH_MALLOCED)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100459 free((char *)lte->hash);
460 elf_end(lte->elf);
461 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200462}
463
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100464static void
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100465add_library_symbol(GElf_Addr addr, const char *name,
466 struct library_symbol **library_symbolspp,
Juan Cespedesf1350522008-12-16 18:19:58 +0100467 enum toplt type_of_plt, int is_weak) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100468 struct library_symbol *s;
Juan Cespedescd8976d2009-05-14 13:47:58 +0200469
470 debug(DEBUG_FUNCTION, "add_library_symbol()");
471
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100472 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
473 if (s == NULL)
474 error(EXIT_FAILURE, errno, "add_library_symbol failed");
475
476 s->needs_init = 1;
477 s->is_weak = is_weak;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200478 s->plt_type = type_of_plt;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100479 s->next = *library_symbolspp;
480 s->enter_addr = (void *)(uintptr_t) addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100481 s->name = (char *)(s + 1);
482 strcpy(s->name, name);
483 *library_symbolspp = s;
484
485 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100486}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200487
Olaf Hering03c087b2006-09-25 02:31:27 +0200488/* stolen from elfutils-0.123 */
Juan Cespedesf1350522008-12-16 18:19:58 +0100489static unsigned long
490private_elf_gnu_hash(const char *name) {
Olaf Hering03c087b2006-09-25 02:31:27 +0200491 unsigned long h = 5381;
492 const unsigned char *string = (const unsigned char *)name;
493 unsigned char c;
494 for (c = *string; c; c = *++string)
495 h = h * 33 + c;
496 return h & 0xffffffff;
497}
498
Juan Cespedesf1350522008-12-16 18:19:58 +0100499static int
Joe Damato3268c5a2010-11-08 15:47:38 -0800500symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym,
501 size_t symidx, const char *name)
502{
503 GElf_Sym tmp_sym;
504 GElf_Sym *tmp;
505
506 tmp = (sym) ? (sym) : (&tmp_sym);
507
508 if (gelf_getsym(lte[lte_i].dynsym, symidx, tmp) == NULL)
509 error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym");
510 else {
511 tmp->st_value += lte[lte_i].base_addr;
512 debug(2, "symbol found: %s, %zd, %lx",
513 name, lte_i, tmp->st_value);
514 }
515 return tmp->st_value != 0
516 && tmp->st_shndx != SHN_UNDEF
517 && strcmp(name, lte[lte_i].dynstr + tmp->st_name) == 0;
518}
519
520static int
521in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100522 size_t i;
523 unsigned long hash;
Petr Machata35fe5182006-07-18 12:58:12 +0200524 unsigned long gnu_hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100525
Joe Damato3268c5a2010-11-08 15:47:38 -0800526 if (!count)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100527 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100528
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200529 hash = elf_hash((const unsigned char *)name);
Petr Machata07bc4d12006-11-30 14:47:40 +0100530 gnu_hash = private_elf_gnu_hash(name);
Joe Damato3268c5a2010-11-08 15:47:38 -0800531
532 for (i = 0; i < count; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100533 if (lte[i].hash == NULL)
534 continue;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200535
Petr Machata35fe5182006-07-18 12:58:12 +0200536 if (lte[i].hash_type == SHT_GNU_HASH) {
537 Elf32_Word * hashbase = lte[i].hash;
538 Elf32_Word nbuckets = *hashbase++;
539 Elf32_Word symbias = *hashbase++;
540 Elf32_Word bitmask_nwords = *hashbase++;
Petr Machata35fe5182006-07-18 12:58:12 +0200541 Elf32_Word * buckets;
542 Elf32_Word * chain_zero;
543 Elf32_Word bucket;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200544
Petr Machatab3f8fef2006-11-30 14:45:07 +0100545 // +1 for skipped `shift'
546 hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
Petr Machata35fe5182006-07-18 12:58:12 +0200547 buckets = hashbase;
548 hashbase += nbuckets;
549 chain_zero = hashbase - symbias;
550 bucket = buckets[gnu_hash % nbuckets];
Juan Cespedesd914a202004-11-10 00:15:33 +0100551
Petr Machata35fe5182006-07-18 12:58:12 +0200552 if (bucket != 0) {
553 const Elf32_Word *hasharr = &chain_zero[bucket];
554 do
555 if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
556 int symidx = hasharr - chain_zero;
Joe Damato3268c5a2010-11-08 15:47:38 -0800557 if (symbol_matches(lte, i,
558 sym, symidx,
559 name))
Petr Machata35fe5182006-07-18 12:58:12 +0200560 return 1;
561 }
562 while ((*hasharr++ & 1u) == 0);
563 }
Olaf Hering03c087b2006-09-25 02:31:27 +0200564 } else {
Petr Machata35fe5182006-07-18 12:58:12 +0200565 Elf32_Word nbuckets, symndx;
566 Elf32_Word *buckets, *chain;
567 nbuckets = lte[i].hash[0];
568 buckets = &lte[i].hash[2];
569 chain = &lte[i].hash[2 + nbuckets];
570
571 for (symndx = buckets[hash % nbuckets];
Joe Damato3268c5a2010-11-08 15:47:38 -0800572 symndx != STN_UNDEF; symndx = chain[symndx])
573 if (symbol_matches(lte, i, sym, symndx, name))
Petr Machata35fe5182006-07-18 12:58:12 +0200574 return 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100575 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200576 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100577 return 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100578}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200579
Juan Cespedesf1350522008-12-16 18:19:58 +0100580static GElf_Addr
581opd2addr(struct ltelf *lte, GElf_Addr addr) {
Petr Machatab3f8fef2006-11-30 14:45:07 +0100582#ifdef ARCH_SUPPORTS_OPD
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200583 unsigned long base, offset;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200584
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100585 if (!lte->opd)
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200586 return addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100587
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200588 base = (unsigned long)lte->opd->d_buf;
589 offset = (unsigned long)addr - (unsigned long)lte->opd_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100590 if (offset > lte->opd_size)
591 error(EXIT_FAILURE, 0, "static plt not in .opd");
592
Olaf Heringa841f652006-09-15 01:57:49 +0200593 return *(GElf_Addr*)(base + offset);
Petr Machatab3f8fef2006-11-30 14:45:07 +0100594#else //!ARCH_SUPPORTS_OPD
595 return addr;
596#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100597}
598
Juan Cespedesf1350522008-12-16 18:19:58 +0100599struct library_symbol *
Juan Cespedesa8909f72009-04-28 20:02:41 +0200600read_elf(Process *proc) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100601 struct library_symbol *library_symbols = NULL;
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
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100611 elf_version(EV_CURRENT);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100612
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100613 do_init_elf(lte, proc->filename);
614 proc->e_machine = lte->ehdr.e_machine;
615 for (i = 0; i < library_num; ++i)
616 do_init_elf(&lte[i + 1], library[i]);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700617
618 if (!options.no_plt) {
Eric Vaitl1228a912006-12-28 16:16:56 +0100619#ifdef __mips__
Joe Damatofa2aefc2010-10-30 19:56:50 -0700620 // MIPS doesn't use the PLT and the GOT entries get changed
621 // on startup.
622 proc->need_to_reinitialize_breakpoints = 1;
623 for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
624 GElf_Sym sym;
625 const char *name;
626 GElf_Addr addr = arch_plt_sym_val(lte, i, 0);
627 if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
628 error(EXIT_FAILURE, 0,
629 "Couldn't get relocation from \"%s\"",
630 proc->filename);
631 }
632 name=lte->dynstr+sym.st_name;
Arnaud Patard95623b22010-01-08 08:40:02 -0500633 if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
634 debug(2,"sym %s not a function",name);
635 continue;
636 }
637 add_library_symbol(addr, name, &library_symbols, 0,
638 ELF64_ST_BIND(sym.st_info) != 0);
639 if (!lib_tail)
640 lib_tail = &(library_symbols->next);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200641 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100642#else
Joe Damatofa2aefc2010-10-30 19:56:50 -0700643 for (i = 0; i < lte->relplt_count; ++i) {
644 GElf_Rel rel;
645 GElf_Rela rela;
646 GElf_Sym sym;
647 GElf_Addr addr;
648 void *ret;
649 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100650
Joe Damatofa2aefc2010-10-30 19:56:50 -0700651 if (lte->relplt->d_type == ELF_T_REL) {
652 ret = gelf_getrel(lte->relplt, i, &rel);
653 rela.r_offset = rel.r_offset;
654 rela.r_info = rel.r_info;
655 rela.r_addend = 0;
656 } else
657 ret = gelf_getrela(lte->relplt, i, &rela);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100658
Joe Damatofa2aefc2010-10-30 19:56:50 -0700659 if (ret == NULL
660 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
661 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
662 &sym) == NULL)
663 error(EXIT_FAILURE, 0,
664 "Couldn't get relocation from \"%s\"",
665 proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100666
Paul Gilliambe320772006-04-24 22:06:23 +0200667#ifdef PLT_REINITALISATION_BP
Joe Damatofa2aefc2010-10-30 19:56:50 -0700668 if (!sym.st_value && PLTs_initialized_by_here)
669 proc->need_to_reinitialize_breakpoints = 1;
Paul Gilliambe320772006-04-24 22:06:23 +0200670#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100671
Joe Damato3268c5a2010-11-08 15:47:38 -0800672 name = lte->dynstr + sym.st_name;
673 count = library_num ? library_num+1 : 0;
Petr Machatafe1c1712010-10-27 16:57:34 +0200674
Joe Damato3268c5a2010-11-08 15:47:38 -0800675 if (in_load_libraries(name, lte, count, NULL)) {
676 enum toplt pltt;
677 if (sym.st_value == 0 && lte->plt_stub_vma != 0) {
678 pltt = LS_TOPLT_EXEC;
679 addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i;
680 }
681 else {
682 pltt = PLTS_ARE_EXECUTABLE(lte)
683 ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
684 addr = arch_plt_sym_val(lte, i, &rela);
685 }
686
687 add_library_symbol(addr, name, &library_symbols, pltt,
688 ELF64_ST_BIND(sym.st_info) == STB_WEAK);
689 if (!lib_tail)
690 lib_tail = &(library_symbols->next);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700691 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100692 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100693#endif // !__mips__
Paul Gilliambe320772006-04-24 22:06:23 +0200694#ifdef PLT_REINITALISATION_BP
Joe Damatofa2aefc2010-10-30 19:56:50 -0700695 struct opt_x_t *main_cheat;
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200696
Joe Damatofa2aefc2010-10-30 19:56:50 -0700697 if (proc->need_to_reinitialize_breakpoints) {
698 /* Add "PLTs_initialized_by_here" to opt_x list, if not
699 already there. */
700 main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
701 if (main_cheat == NULL)
702 error(EXIT_FAILURE, 0, "Couldn't allocate memory");
703 main_cheat->next = opt_x;
704 main_cheat->found = 0;
705 main_cheat->name = PLTs_initialized_by_here;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100706
Joe Damatofa2aefc2010-10-30 19:56:50 -0700707 for (xptr = opt_x; xptr; xptr = xptr->next)
708 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
709 && main_cheat) {
710 free(main_cheat);
711 main_cheat = NULL;
712 break;
713 }
714 if (main_cheat)
715 opt_x = main_cheat;
716 }
Paul Gilliambe320772006-04-24 22:06:23 +0200717#endif
Joe Damatofa2aefc2010-10-30 19:56:50 -0700718 } else {
719 lib_tail = &library_symbols;
720 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100721
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100722 for (i = 0; i < lte->symtab_count; ++i) {
723 GElf_Sym sym;
724 GElf_Addr addr;
725 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100726
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100727 if (gelf_getsym(lte->symtab, i, &sym) == NULL)
728 error(EXIT_FAILURE, 0,
729 "Couldn't get symbol from \"%s\"",
730 proc->filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100731
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100732 name = lte->strtab + sym.st_name;
733 addr = sym.st_value;
734 if (!addr)
735 continue;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100736
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100737 for (xptr = opt_x; xptr; xptr = xptr->next)
738 if (xptr->name && strcmp(xptr->name, name) == 0) {
739 /* FIXME: Should be able to use &library_symbols as above. But
740 when you do, none of the real library symbols cause breaks. */
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200741 add_library_symbol(opd2addr(lte, addr),
Paul Gilliam76c61f12006-06-14 06:55:21 +0200742 name, lib_tail, LS_TOPLT_NONE, 0);
Paul Gilliam24e643a2006-03-13 18:43:13 +0100743 xptr->found = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100744 break;
745 }
746 }
747 for (xptr = opt_x; xptr; xptr = xptr->next)
Paul Gilliam24e643a2006-03-13 18:43:13 +0100748 if ( ! xptr->found) {
749 char *badthing = "WARNING";
Paul Gilliambe320772006-04-24 22:06:23 +0200750#ifdef PLT_REINITALISATION_BP
751 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
752 if (lte->ehdr.e_entry) {
753 add_library_symbol (
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200754 opd2addr (lte, lte->ehdr.e_entry),
Paul Gilliambe320772006-04-24 22:06:23 +0200755 PLTs_initialized_by_here,
756 lib_tail, 1, 0);
757 fprintf (stderr, "WARNING: Using e_ent"
758 "ry from elf header (%p) for "
759 "address of \"%s\"\n", (void*)
760 (long) lte->ehdr.e_entry,
761 PLTs_initialized_by_here);
762 continue;
763 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100764 badthing = "ERROR";
765 exit_out = 1;
766 }
Paul Gilliambe320772006-04-24 22:06:23 +0200767#endif
Paul Gilliam24e643a2006-03-13 18:43:13 +0100768 fprintf (stderr,
Paul Gilliambe320772006-04-24 22:06:23 +0200769 "%s: Couldn't find symbol \"%s\" in file \"%s"
770 "\"\n", badthing, xptr->name, proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100771 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100772 if (exit_out) {
773 exit (1);
774 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100775
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100776 for (i = 0; i < library_num + 1; ++i)
777 do_close_elf(&lte[i]);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100778
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100779 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200780}