blob: 29e9c9c783b9175616463f541d2c1f171d52e1ab [file] [log] [blame]
Joe Damatof0bd98b2010-11-08 15:47:42 -08001#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;
Joe Damatof0bd98b2010-11-08 15:47:42 -080025struct ltelf main_lte;
26
Paul Gilliambe320772006-04-24 22:06:23 +020027#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010028extern char *PLTs_initialized_by_here;
Paul Gilliambe320772006-04-24 22:06:23 +020029#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010030
Petr Machatafe1c1712010-10-27 16:57:34 +020031#ifndef DT_PPC_GOT
32# define DT_PPC_GOT (DT_LOPROC + 0)
33#endif
34
35#define PPC_PLT_STUB_SIZE 16
36
37static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
38{
39 Elf_Data *data = elf_getdata(scn, NULL);
40 if (data == NULL || elf_getdata(scn, data) != NULL
41 || data->d_off || data->d_size != shdr->sh_size)
42 return NULL;
43 return data;
44}
45
46static int inside(GElf_Addr addr, GElf_Shdr *shdr)
47{
48 return addr >= shdr->sh_addr
49 && addr < shdr->sh_addr + shdr->sh_size;
50}
51
52static int maybe_pick_section(GElf_Addr addr,
53 Elf_Scn *in_sec, GElf_Shdr *in_shdr,
54 Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
55{
56 if (inside (addr, in_shdr)) {
57 *tgt_sec = in_sec;
58 *tgt_shdr = *in_shdr;
59 return 1;
60 }
61 return 0;
62}
63
64static int get_section_covering(struct ltelf *lte, GElf_Addr addr,
65 Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
66{
67 int i;
68 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
69 Elf_Scn *scn;
70 GElf_Shdr shdr;
71
72 scn = elf_getscn(lte->elf, i);
73 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
74 debug(1, "Couldn't read section or header.");
75 return 0;
76 }
77
78 if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr))
79 return 1;
80 }
81
82 return 0;
83}
84
85static GElf_Addr read32be(Elf_Data *data, size_t offset)
86{
87 if (data->d_size < offset + 4) {
88 debug(1, "Not enough data to read 32bit value at offset %zd.",
89 offset);
90 return 0;
91 }
92
93 unsigned char const *buf = data->d_buf + offset;
94 return ((Elf32_Word)buf[0] << 24)
95 | ((Elf32_Word)buf[1] << 16)
96 | ((Elf32_Word)buf[2] << 8)
97 | ((Elf32_Word)buf[3]);
98}
99
100static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot,
101 Elf_Data *plt_data)
102{
103 Elf_Scn *ppcgot_sec = NULL;
104 GElf_Shdr ppcgot_shdr;
105 if (ppcgot != 0
106 && !get_section_covering(lte, ppcgot, &ppcgot_sec, &ppcgot_shdr))
107 // xxx should be the log out
108 fprintf(stderr,
109 "DT_PPC_GOT=%#lx, but no such section found.\n",
110 ppcgot);
111
112 if (ppcgot_sec != NULL) {
113 Elf_Data *data = loaddata(ppcgot_sec, &ppcgot_shdr);
114 if (data == NULL
115 || data->d_size < 8 )
116 debug(1, "Couldn't read GOT data.");
117 else {
118 // where PPCGOT begins in .got
119 size_t offset = ppcgot - ppcgot_shdr.sh_addr;
120 GElf_Addr glink_vma = read32be(data, offset + 4);
121 if (glink_vma != 0) {
122 debug(1, "PPC GOT glink_vma address: %#lx",
123 glink_vma);
124 return glink_vma;
125 }
126 }
127 }
128
129 if (plt_data != NULL) {
130 GElf_Addr glink_vma = read32be(plt_data, 0);
131 debug(1, ".plt glink_vma address: %#lx", glink_vma);
132 return glink_vma;
133 }
134
135 return 0;
136}
137
Joe Damato7a2bdf82010-11-08 15:47:41 -0800138void
Juan Cespedesf1350522008-12-16 18:19:58 +0100139do_init_elf(struct ltelf *lte, const char *filename) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100140 int i;
141 GElf_Addr relplt_addr = 0;
142 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200143
Juan Cespedescd8976d2009-05-14 13:47:58 +0200144 debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100145 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +0200146
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100147 lte->fd = open(filename, O_RDONLY);
148 if (lte->fd == -1)
149 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +0200150
Juan Cespedesd914a202004-11-10 00:15:33 +0100151#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100152 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200153#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100154 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200155#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200156
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100157 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
158 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200159
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100160 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
161 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
162 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200163
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100164 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
165 error(EXIT_FAILURE, 0,
166 "\"%s\" is not an ELF executable nor shared library",
167 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200168
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100169 if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
170 || lte->ehdr.e_machine != LT_ELF_MACHINE)
Juan Cespedesd914a202004-11-10 00:15:33 +0100171#ifdef LT_ELF_MACHINE2
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100172 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
173 || lte->ehdr.e_machine != LT_ELF_MACHINE2)
Juan Cespedesd914a202004-11-10 00:15:33 +0100174#endif
175#ifdef LT_ELF_MACHINE3
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100176 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3
177 || lte->ehdr.e_machine != LT_ELF_MACHINE3)
Juan Cespedesd914a202004-11-10 00:15:33 +0100178#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100179 )
180 error(EXIT_FAILURE, 0,
181 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100182
Petr Machatafe1c1712010-10-27 16:57:34 +0200183 Elf_Data *plt_data = NULL;
184 GElf_Addr ppcgot = 0;
185
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100186 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
187 Elf_Scn *scn;
188 GElf_Shdr shdr;
189 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +0100190
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100191 scn = elf_getscn(lte->elf, i);
192 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
193 error(EXIT_FAILURE, 0,
194 "Couldn't get section header from \"%s\"",
195 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100196
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100197 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
198 if (name == NULL)
199 error(EXIT_FAILURE, 0,
200 "Couldn't get section header from \"%s\"",
201 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100202
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100203 if (shdr.sh_type == SHT_SYMTAB) {
204 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100205
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100206 lte->symtab = elf_getdata(scn, NULL);
207 lte->symtab_count = shdr.sh_size / shdr.sh_entsize;
208 if ((lte->symtab == NULL
209 || elf_getdata(scn, lte->symtab) != NULL)
210 && opt_x != NULL)
211 error(EXIT_FAILURE, 0,
212 "Couldn't get .symtab data from \"%s\"",
213 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100214
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100215 scn = elf_getscn(lte->elf, shdr.sh_link);
216 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
217 error(EXIT_FAILURE, 0,
218 "Couldn't get section header from \"%s\"",
219 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100220
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100221 data = elf_getdata(scn, NULL);
222 if (data == NULL || elf_getdata(scn, data) != NULL
223 || shdr.sh_size != data->d_size || data->d_off)
224 error(EXIT_FAILURE, 0,
225 "Couldn't get .strtab data from \"%s\"",
226 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100227
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100228 lte->strtab = data->d_buf;
229 } else if (shdr.sh_type == SHT_DYNSYM) {
230 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100231
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100232 lte->dynsym = elf_getdata(scn, NULL);
233 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
234 if (lte->dynsym == NULL
235 || elf_getdata(scn, lte->dynsym) != NULL)
236 error(EXIT_FAILURE, 0,
237 "Couldn't get .dynsym data from \"%s\"",
238 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100239
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100240 scn = elf_getscn(lte->elf, shdr.sh_link);
241 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
242 error(EXIT_FAILURE, 0,
243 "Couldn't get section header from \"%s\"",
244 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100245
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100246 data = elf_getdata(scn, NULL);
247 if (data == NULL || elf_getdata(scn, data) != NULL
248 || shdr.sh_size != data->d_size || data->d_off)
249 error(EXIT_FAILURE, 0,
250 "Couldn't get .dynstr data from \"%s\"",
251 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100252
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100253 lte->dynstr = data->d_buf;
254 } else if (shdr.sh_type == SHT_DYNAMIC) {
255 Elf_Data *data;
256 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100257
Joe Damato87f4f582010-11-08 15:47:36 -0800258 lte->dyn_addr = shdr.sh_addr;
259 lte->dyn_sz = shdr.sh_size;
260
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100261 data = elf_getdata(scn, NULL);
262 if (data == NULL || elf_getdata(scn, data) != NULL)
263 error(EXIT_FAILURE, 0,
264 "Couldn't get .dynamic data from \"%s\"",
265 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100266
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100267 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
268 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100269
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100270 if (gelf_getdyn(data, j, &dyn) == NULL)
271 error(EXIT_FAILURE, 0,
272 "Couldn't get .dynamic data from \"%s\"",
273 filename);
Eric Vaitl1228a912006-12-28 16:16:56 +0100274#ifdef __mips__
275/**
276 MIPS ABI Supplement:
Ian Wienand9a2ad352006-02-20 22:44:45 +0100277
Juan Cespedesf1350522008-12-16 18:19:58 +0100278 DT_PLTGOT This member holds the address of the .got section.
Eric Vaitl1228a912006-12-28 16:16:56 +0100279
280 DT_MIPS_SYMTABNO This member holds the number of entries in the
281 .dynsym section.
282
283 DT_MIPS_LOCAL_GOTNO This member holds the number of local global
284 offset table entries.
285
286 DT_MIPS_GOTSYM This member holds the index of the first dyamic
287 symbol table entry that corresponds to an entry in the gobal offset
288 table.
289
290 */
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200291 if(dyn.d_tag==DT_PLTGOT){
Juan Cespedesf1350522008-12-16 18:19:58 +0100292 lte->pltgot_addr=dyn.d_un.d_ptr;
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200293 }
294 if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
295 lte->mips_local_gotno=dyn.d_un.d_val;
296 }
297 if(dyn.d_tag==DT_MIPS_GOTSYM){
298 lte->mips_gotsym=dyn.d_un.d_val;
299 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100300#endif // __mips__
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100301 if (dyn.d_tag == DT_JMPREL)
302 relplt_addr = dyn.d_un.d_ptr;
303 else if (dyn.d_tag == DT_PLTRELSZ)
304 relplt_size = dyn.d_un.d_val;
Petr Machatafe1c1712010-10-27 16:57:34 +0200305 else if (dyn.d_tag == DT_PPC_GOT) {
306 ppcgot = dyn.d_un.d_val;
307 debug(1, "ppcgot %#lx", ppcgot);
308 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100309 }
310 } else if (shdr.sh_type == SHT_HASH) {
311 Elf_Data *data;
312 size_t j;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100313
Petr Machata35fe5182006-07-18 12:58:12 +0200314 lte->hash_type = SHT_HASH;
315
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100316 data = elf_getdata(scn, NULL);
317 if (data == NULL || elf_getdata(scn, data) != NULL
318 || data->d_off || data->d_size != shdr.sh_size)
319 error(EXIT_FAILURE, 0,
320 "Couldn't get .hash data from \"%s\"",
321 filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100322
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100323 if (shdr.sh_entsize == 4) {
324 /* Standard conforming ELF. */
325 if (data->d_type != ELF_T_WORD)
326 error(EXIT_FAILURE, 0,
327 "Couldn't get .hash data from \"%s\"",
328 filename);
329 lte->hash = (Elf32_Word *) data->d_buf;
330 } else if (shdr.sh_entsize == 8) {
331 /* Alpha or s390x. */
332 Elf32_Word *dst, *src;
333 size_t hash_count = data->d_size / 8;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100334
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100335 lte->hash = (Elf32_Word *)
336 malloc(hash_count * sizeof(Elf32_Word));
337 if (lte->hash == NULL)
338 error(EXIT_FAILURE, 0,
339 "Couldn't convert .hash section from \"%s\"",
340 filename);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200341 lte->lte_flags |= LTE_HASH_MALLOCED;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100342 dst = lte->hash;
343 src = (Elf32_Word *) data->d_buf;
344 if ((data->d_type == ELF_T_WORD
345 && __BYTE_ORDER == __BIG_ENDIAN)
346 || (data->d_type == ELF_T_XWORD
347 && lte->ehdr.e_ident[EI_DATA] ==
348 ELFDATA2MSB))
349 ++src;
350 for (j = 0; j < hash_count; ++j, src += 2)
351 *dst++ = *src;
352 } else
353 error(EXIT_FAILURE, 0,
354 "Unknown .hash sh_entsize in \"%s\"",
355 filename);
Petr Machata35fe5182006-07-18 12:58:12 +0200356 } else if (shdr.sh_type == SHT_GNU_HASH
357 && lte->hash == NULL) {
358 Elf_Data *data;
Petr Machata35fe5182006-07-18 12:58:12 +0200359
360 lte->hash_type = SHT_GNU_HASH;
361
362 if (shdr.sh_entsize != 0
363 && shdr.sh_entsize != 4) {
364 error(EXIT_FAILURE, 0,
Olaf Hering03c087b2006-09-25 02:31:27 +0200365 ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu",
Petr Machata35fe5182006-07-18 12:58:12 +0200366 filename, shdr.sh_entsize);
367 }
368
Petr Machatafe1c1712010-10-27 16:57:34 +0200369 data = loaddata(scn, &shdr);
370 if (data == NULL)
Petr Machata35fe5182006-07-18 12:58:12 +0200371 error(EXIT_FAILURE, 0,
372 "Couldn't get .gnu.hash data from \"%s\"",
373 filename);
374
375 lte->hash = (Elf32_Word *) data->d_buf;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100376 } else if (shdr.sh_type == SHT_PROGBITS
377 || shdr.sh_type == SHT_NOBITS) {
378 if (strcmp(name, ".plt") == 0) {
379 lte->plt_addr = shdr.sh_addr;
380 lte->plt_size = shdr.sh_size;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200381 if (shdr.sh_flags & SHF_EXECINSTR) {
382 lte->lte_flags |= LTE_PLT_EXECUTABLE;
383 }
Petr Machatafe1c1712010-10-27 16:57:34 +0200384 if (lte->ehdr.e_machine == EM_PPC) {
385 plt_data = loaddata(scn, &shdr);
386 if (plt_data == NULL)
387 fprintf(stderr,
388 "Can't load .plt data\n");
389 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100390 }
391#ifdef ARCH_SUPPORTS_OPD
392 else if (strcmp(name, ".opd") == 0) {
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200393 lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100394 lte->opd_size = shdr.sh_size;
395 lte->opd = elf_rawdata(scn, NULL);
396 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100397#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100398 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100399 }
400
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100401 if (lte->dynsym == NULL || lte->dynstr == NULL)
402 error(EXIT_FAILURE, 0,
403 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100404
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100405 if (!relplt_addr || !lte->plt_addr) {
406 debug(1, "%s has no PLT relocations", filename);
407 lte->relplt = NULL;
408 lte->relplt_count = 0;
Petr Machatafe1c1712010-10-27 16:57:34 +0200409 } else if (relplt_size == 0) {
410 debug(1, "%s has unknown PLT size", filename);
411 lte->relplt = NULL;
412 lte->relplt_count = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100413 } else {
Petr Machatafe1c1712010-10-27 16:57:34 +0200414 if (lte->ehdr.e_machine == EM_PPC) {
415 GElf_Addr glink_vma
416 = get_glink_vma(lte, ppcgot, plt_data);
417
418 assert (relplt_size % 12 == 0);
419 size_t count = relplt_size / 12; // size of RELA entry
420 lte->plt_stub_vma = glink_vma
421 - (GElf_Addr)count * PPC_PLT_STUB_SIZE;
422 debug(1, "stub_vma is %#lx", lte->plt_stub_vma);
423 }
424
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100425 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
426 Elf_Scn *scn;
427 GElf_Shdr shdr;
428
429 scn = elf_getscn(lte->elf, i);
430 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
431 error(EXIT_FAILURE, 0,
432 "Couldn't get section header from \"%s\"",
433 filename);
434 if (shdr.sh_addr == relplt_addr
435 && shdr.sh_size == relplt_size) {
436 lte->relplt = elf_getdata(scn, NULL);
437 lte->relplt_count =
438 shdr.sh_size / shdr.sh_entsize;
439 if (lte->relplt == NULL
440 || elf_getdata(scn, lte->relplt) != NULL)
441 error(EXIT_FAILURE, 0,
442 "Couldn't get .rel*.plt data from \"%s\"",
443 filename);
444 break;
445 }
446 }
447
448 if (i == lte->ehdr.e_shnum)
449 error(EXIT_FAILURE, 0,
450 "Couldn't find .rel*.plt section in \"%s\"",
451 filename);
452
453 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
454 }
455}
456
Joe Damato7a2bdf82010-11-08 15:47:41 -0800457void
Juan Cespedesf1350522008-12-16 18:19:58 +0100458do_close_elf(struct ltelf *lte) {
Juan Cespedescd8976d2009-05-14 13:47:58 +0200459 debug(DEBUG_FUNCTION, "do_close_elf()");
Paul Gilliam76c61f12006-06-14 06:55:21 +0200460 if (lte->lte_flags & LTE_HASH_MALLOCED)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100461 free((char *)lte->hash);
462 elf_end(lte->elf);
463 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200464}
465
Joe Damato7a2bdf82010-11-08 15:47:41 -0800466void
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100467add_library_symbol(GElf_Addr addr, const char *name,
468 struct library_symbol **library_symbolspp,
Juan Cespedesf1350522008-12-16 18:19:58 +0100469 enum toplt type_of_plt, int is_weak) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100470 struct library_symbol *s;
Juan Cespedescd8976d2009-05-14 13:47:58 +0200471
472 debug(DEBUG_FUNCTION, "add_library_symbol()");
473
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100474 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
475 if (s == NULL)
476 error(EXIT_FAILURE, errno, "add_library_symbol failed");
477
478 s->needs_init = 1;
479 s->is_weak = is_weak;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200480 s->plt_type = type_of_plt;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100481 s->next = *library_symbolspp;
482 s->enter_addr = (void *)(uintptr_t) addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100483 s->name = (char *)(s + 1);
484 strcpy(s->name, name);
485 *library_symbolspp = s;
486
487 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100488}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200489
Olaf Hering03c087b2006-09-25 02:31:27 +0200490/* stolen from elfutils-0.123 */
Juan Cespedesf1350522008-12-16 18:19:58 +0100491static unsigned long
492private_elf_gnu_hash(const char *name) {
Olaf Hering03c087b2006-09-25 02:31:27 +0200493 unsigned long h = 5381;
494 const unsigned char *string = (const unsigned char *)name;
495 unsigned char c;
496 for (c = *string; c; c = *++string)
497 h = h * 33 + c;
498 return h & 0xffffffff;
499}
500
Juan Cespedesf1350522008-12-16 18:19:58 +0100501static int
Joe Damato3268c5a2010-11-08 15:47:38 -0800502symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym,
503 size_t symidx, const char *name)
504{
505 GElf_Sym tmp_sym;
506 GElf_Sym *tmp;
507
508 tmp = (sym) ? (sym) : (&tmp_sym);
509
510 if (gelf_getsym(lte[lte_i].dynsym, symidx, tmp) == NULL)
511 error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym");
512 else {
513 tmp->st_value += lte[lte_i].base_addr;
514 debug(2, "symbol found: %s, %zd, %lx",
515 name, lte_i, tmp->st_value);
516 }
517 return tmp->st_value != 0
518 && tmp->st_shndx != SHN_UNDEF
519 && strcmp(name, lte[lte_i].dynstr + tmp->st_name) == 0;
520}
521
Joe Damato7a2bdf82010-11-08 15:47:41 -0800522int
Joe Damato3268c5a2010-11-08 15:47:38 -0800523in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100524 size_t i;
525 unsigned long hash;
Petr Machata35fe5182006-07-18 12:58:12 +0200526 unsigned long gnu_hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100527
Joe Damato3268c5a2010-11-08 15:47:38 -0800528 if (!count)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100529 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100530
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200531 hash = elf_hash((const unsigned char *)name);
Petr Machata07bc4d12006-11-30 14:47:40 +0100532 gnu_hash = private_elf_gnu_hash(name);
Joe Damato3268c5a2010-11-08 15:47:38 -0800533
534 for (i = 0; i < count; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100535 if (lte[i].hash == NULL)
536 continue;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200537
Petr Machata35fe5182006-07-18 12:58:12 +0200538 if (lte[i].hash_type == SHT_GNU_HASH) {
539 Elf32_Word * hashbase = lte[i].hash;
540 Elf32_Word nbuckets = *hashbase++;
541 Elf32_Word symbias = *hashbase++;
542 Elf32_Word bitmask_nwords = *hashbase++;
Petr Machata35fe5182006-07-18 12:58:12 +0200543 Elf32_Word * buckets;
544 Elf32_Word * chain_zero;
545 Elf32_Word bucket;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200546
Petr Machatab3f8fef2006-11-30 14:45:07 +0100547 // +1 for skipped `shift'
548 hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
Petr Machata35fe5182006-07-18 12:58:12 +0200549 buckets = hashbase;
550 hashbase += nbuckets;
551 chain_zero = hashbase - symbias;
552 bucket = buckets[gnu_hash % nbuckets];
Juan Cespedesd914a202004-11-10 00:15:33 +0100553
Petr Machata35fe5182006-07-18 12:58:12 +0200554 if (bucket != 0) {
555 const Elf32_Word *hasharr = &chain_zero[bucket];
556 do
557 if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
558 int symidx = hasharr - chain_zero;
Joe Damato3268c5a2010-11-08 15:47:38 -0800559 if (symbol_matches(lte, i,
560 sym, symidx,
561 name))
Petr Machata35fe5182006-07-18 12:58:12 +0200562 return 1;
563 }
564 while ((*hasharr++ & 1u) == 0);
565 }
Olaf Hering03c087b2006-09-25 02:31:27 +0200566 } else {
Petr Machata35fe5182006-07-18 12:58:12 +0200567 Elf32_Word nbuckets, symndx;
568 Elf32_Word *buckets, *chain;
569 nbuckets = lte[i].hash[0];
570 buckets = &lte[i].hash[2];
571 chain = &lte[i].hash[2 + nbuckets];
572
573 for (symndx = buckets[hash % nbuckets];
Joe Damato3268c5a2010-11-08 15:47:38 -0800574 symndx != STN_UNDEF; symndx = chain[symndx])
575 if (symbol_matches(lte, i, sym, symndx, name))
Petr Machata35fe5182006-07-18 12:58:12 +0200576 return 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100577 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200578 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100579 return 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100580}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200581
Juan Cespedesf1350522008-12-16 18:19:58 +0100582static GElf_Addr
583opd2addr(struct ltelf *lte, GElf_Addr addr) {
Petr Machatab3f8fef2006-11-30 14:45:07 +0100584#ifdef ARCH_SUPPORTS_OPD
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200585 unsigned long base, offset;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200586
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100587 if (!lte->opd)
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200588 return addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100589
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200590 base = (unsigned long)lte->opd->d_buf;
591 offset = (unsigned long)addr - (unsigned long)lte->opd_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100592 if (offset > lte->opd_size)
593 error(EXIT_FAILURE, 0, "static plt not in .opd");
594
Olaf Heringa841f652006-09-15 01:57:49 +0200595 return *(GElf_Addr*)(base + offset);
Petr Machatab3f8fef2006-11-30 14:45:07 +0100596#else //!ARCH_SUPPORTS_OPD
597 return addr;
598#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100599}
600
Juan Cespedesf1350522008-12-16 18:19:58 +0100601struct library_symbol *
Juan Cespedesa8909f72009-04-28 20:02:41 +0200602read_elf(Process *proc) {
Juan Cespedes8d1b92b2009-07-03 10:39:34 +0200603 struct ltelf lte[MAX_LIBRARIES + 1];
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100604 size_t i;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100605 struct opt_x_t *xptr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100606 struct library_symbol **lib_tail = NULL;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100607 int exit_out = 0;
Joe Damato3268c5a2010-11-08 15:47:38 -0800608 int count = 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100609
Juan Cespedescd8976d2009-05-14 13:47:58 +0200610 debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename);
611
Joe Damatof0bd98b2010-11-08 15:47:42 -0800612 memset(lte, 0, sizeof(*lte));
Joe Damato7a2bdf82010-11-08 15:47:41 -0800613 library_symbols = NULL;
614 library_num = 0;
Joe Damatof0bd98b2010-11-08 15:47:42 -0800615 proc->libdl_hooked = 0;
616
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100617 elf_version(EV_CURRENT);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100618
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100619 do_init_elf(lte, proc->filename);
Joe Damatof0bd98b2010-11-08 15:47:42 -0800620
621 memcpy(&main_lte, lte, sizeof(struct ltelf));
622
623 if (opt_p && opt_p->pid > 0) {
624 linkmap_init(proc, lte);
625 proc->libdl_hooked = 1;
626 }
627
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100628 proc->e_machine = lte->ehdr.e_machine;
Joe Damatof0bd98b2010-11-08 15:47:42 -0800629
630 for (i = 0; i < library_num; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100631 do_init_elf(&lte[i + 1], library[i]);
Joe Damatof0bd98b2010-11-08 15:47:42 -0800632 }
Joe Damatofa2aefc2010-10-30 19:56:50 -0700633
634 if (!options.no_plt) {
Eric Vaitl1228a912006-12-28 16:16:56 +0100635#ifdef __mips__
Joe Damatofa2aefc2010-10-30 19:56:50 -0700636 // MIPS doesn't use the PLT and the GOT entries get changed
637 // on startup.
638 proc->need_to_reinitialize_breakpoints = 1;
639 for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
640 GElf_Sym sym;
641 const char *name;
642 GElf_Addr addr = arch_plt_sym_val(lte, i, 0);
643 if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
644 error(EXIT_FAILURE, 0,
645 "Couldn't get relocation from \"%s\"",
646 proc->filename);
647 }
648 name=lte->dynstr+sym.st_name;
Arnaud Patard95623b22010-01-08 08:40:02 -0500649 if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
650 debug(2,"sym %s not a function",name);
651 continue;
652 }
653 add_library_symbol(addr, name, &library_symbols, 0,
654 ELF64_ST_BIND(sym.st_info) != 0);
655 if (!lib_tail)
656 lib_tail = &(library_symbols->next);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200657 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100658#else
Joe Damatofa2aefc2010-10-30 19:56:50 -0700659 for (i = 0; i < lte->relplt_count; ++i) {
660 GElf_Rel rel;
661 GElf_Rela rela;
662 GElf_Sym sym;
663 GElf_Addr addr;
664 void *ret;
665 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100666
Joe Damatofa2aefc2010-10-30 19:56:50 -0700667 if (lte->relplt->d_type == ELF_T_REL) {
668 ret = gelf_getrel(lte->relplt, i, &rel);
669 rela.r_offset = rel.r_offset;
670 rela.r_info = rel.r_info;
671 rela.r_addend = 0;
672 } else
673 ret = gelf_getrela(lte->relplt, i, &rela);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100674
Joe Damatofa2aefc2010-10-30 19:56:50 -0700675 if (ret == NULL
676 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
677 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
678 &sym) == NULL)
679 error(EXIT_FAILURE, 0,
680 "Couldn't get relocation from \"%s\"",
681 proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100682
Paul Gilliambe320772006-04-24 22:06:23 +0200683#ifdef PLT_REINITALISATION_BP
Joe Damatofa2aefc2010-10-30 19:56:50 -0700684 if (!sym.st_value && PLTs_initialized_by_here)
685 proc->need_to_reinitialize_breakpoints = 1;
Paul Gilliambe320772006-04-24 22:06:23 +0200686#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100687
Joe Damato3268c5a2010-11-08 15:47:38 -0800688 name = lte->dynstr + sym.st_name;
689 count = library_num ? library_num+1 : 0;
Petr Machatafe1c1712010-10-27 16:57:34 +0200690
Joe Damato3268c5a2010-11-08 15:47:38 -0800691 if (in_load_libraries(name, lte, count, NULL)) {
692 enum toplt pltt;
693 if (sym.st_value == 0 && lte->plt_stub_vma != 0) {
694 pltt = LS_TOPLT_EXEC;
695 addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i;
696 }
697 else {
698 pltt = PLTS_ARE_EXECUTABLE(lte)
699 ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
700 addr = arch_plt_sym_val(lte, i, &rela);
701 }
702
703 add_library_symbol(addr, name, &library_symbols, pltt,
704 ELF64_ST_BIND(sym.st_info) == STB_WEAK);
705 if (!lib_tail)
706 lib_tail = &(library_symbols->next);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700707 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100708 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100709#endif // !__mips__
Paul Gilliambe320772006-04-24 22:06:23 +0200710#ifdef PLT_REINITALISATION_BP
Joe Damatofa2aefc2010-10-30 19:56:50 -0700711 struct opt_x_t *main_cheat;
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200712
Joe Damatofa2aefc2010-10-30 19:56:50 -0700713 if (proc->need_to_reinitialize_breakpoints) {
714 /* Add "PLTs_initialized_by_here" to opt_x list, if not
715 already there. */
716 main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
717 if (main_cheat == NULL)
718 error(EXIT_FAILURE, 0, "Couldn't allocate memory");
719 main_cheat->next = opt_x;
720 main_cheat->found = 0;
721 main_cheat->name = PLTs_initialized_by_here;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100722
Joe Damatofa2aefc2010-10-30 19:56:50 -0700723 for (xptr = opt_x; xptr; xptr = xptr->next)
724 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
725 && main_cheat) {
726 free(main_cheat);
727 main_cheat = NULL;
728 break;
729 }
730 if (main_cheat)
731 opt_x = main_cheat;
732 }
Paul Gilliambe320772006-04-24 22:06:23 +0200733#endif
Joe Damatofa2aefc2010-10-30 19:56:50 -0700734 } else {
735 lib_tail = &library_symbols;
736 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100737
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100738 for (i = 0; i < lte->symtab_count; ++i) {
739 GElf_Sym sym;
740 GElf_Addr addr;
741 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100742
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100743 if (gelf_getsym(lte->symtab, i, &sym) == NULL)
744 error(EXIT_FAILURE, 0,
745 "Couldn't get symbol from \"%s\"",
746 proc->filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100747
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100748 name = lte->strtab + sym.st_name;
749 addr = sym.st_value;
750 if (!addr)
751 continue;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100752
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100753 for (xptr = opt_x; xptr; xptr = xptr->next)
754 if (xptr->name && strcmp(xptr->name, name) == 0) {
755 /* FIXME: Should be able to use &library_symbols as above. But
756 when you do, none of the real library symbols cause breaks. */
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200757 add_library_symbol(opd2addr(lte, addr),
Paul Gilliam76c61f12006-06-14 06:55:21 +0200758 name, lib_tail, LS_TOPLT_NONE, 0);
Paul Gilliam24e643a2006-03-13 18:43:13 +0100759 xptr->found = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100760 break;
761 }
762 }
Joe Damatoe2a8f572010-11-08 15:47:40 -0800763
764 int found_count = 0;
765
766 for (xptr = opt_x; xptr; xptr = xptr->next) {
767 if (xptr->found)
768 continue;
769
770 GElf_Sym sym;
771 GElf_Addr addr;
772 if (in_load_libraries(xptr->name, lte, library_num+1, &sym)) {
773 debug(2, "found symbol %s @ %lx, adding it.", xptr->name, sym.st_value);
774 addr = sym.st_value;
775 if (ELF32_ST_TYPE (sym.st_info) == STT_FUNC) {
776 add_library_symbol(addr, xptr->name, lib_tail, LS_TOPLT_NONE, 0);
777 xptr->found = 1;
778 found_count++;
779 }
780 }
781 if (found_count == opt_x_cnt){
782 debug(2, "done, found everything: %d\n", found_count);
783 break;
784 }
785 }
786
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100787 for (xptr = opt_x; xptr; xptr = xptr->next)
Paul Gilliam24e643a2006-03-13 18:43:13 +0100788 if ( ! xptr->found) {
789 char *badthing = "WARNING";
Paul Gilliambe320772006-04-24 22:06:23 +0200790#ifdef PLT_REINITALISATION_BP
791 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
792 if (lte->ehdr.e_entry) {
793 add_library_symbol (
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200794 opd2addr (lte, lte->ehdr.e_entry),
Paul Gilliambe320772006-04-24 22:06:23 +0200795 PLTs_initialized_by_here,
796 lib_tail, 1, 0);
797 fprintf (stderr, "WARNING: Using e_ent"
798 "ry from elf header (%p) for "
799 "address of \"%s\"\n", (void*)
800 (long) lte->ehdr.e_entry,
801 PLTs_initialized_by_here);
802 continue;
803 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100804 badthing = "ERROR";
805 exit_out = 1;
806 }
Paul Gilliambe320772006-04-24 22:06:23 +0200807#endif
Paul Gilliam24e643a2006-03-13 18:43:13 +0100808 fprintf (stderr,
Joe Damatof0bd98b2010-11-08 15:47:42 -0800809 "%s: Couldn't find symbol \"%s\" in file \"%s\" assuming it will be loaded by libdl!"
810 "\n", badthing, xptr->name, proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100811 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100812 if (exit_out) {
813 exit (1);
814 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100815
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100816 for (i = 0; i < library_num + 1; ++i)
817 do_close_elf(&lte[i]);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100818
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100819 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200820}