blob: 6d50e92eacf0fec4d4e8e8cd603938281ef05dd2 [file] [log] [blame]
Juan Cespedesd44c6b81998-09-25 14:48:42 +02001#if HAVE_CONFIG_H
Juan Cespedesd914a202004-11-10 00:15:33 +01002# include "config.h"
Juan Cespedesd44c6b81998-09-25 14:48:42 +02003#endif
4
Juan Cespedesd914a202004-11-10 00:15:33 +01005#include <endian.h>
Juan Cespedes96935a91997-08-09 23:45:39 +02006#include <errno.h>
Juan Cespedesd914a202004-11-10 00:15:33 +01007#include <error.h>
Juan Cespedes96935a91997-08-09 23:45:39 +02008#include <fcntl.h>
Juan Cespedesd914a202004-11-10 00:15:33 +01009#include <gelf.h>
10#include <stdint.h>
11#include <stdlib.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020012#include <string.h>
Juan Cespedes5e01f651998-03-08 22:31:44 +010013#include <unistd.h>
Juan Cespedes96935a91997-08-09 23:45:39 +020014
Juan Cespedes96935a91997-08-09 23:45:39 +020015#include "ltrace.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010016#include "elf.h"
Juan Cespedescac15c32003-01-31 18:58:58 +010017#include "debug.h"
Ian Wienand9a2ad352006-02-20 22:44:45 +010018#include "options.h"
Juan Cespedes96935a91997-08-09 23:45:39 +020019
Ian Wienand2d45b1a2006-02-20 22:48:07 +010020static void do_init_elf(struct ltelf *lte, const char *filename);
21static void do_close_elf(struct ltelf *lte);
22static void add_library_symbol(GElf_Addr addr, const char *name,
23 struct library_symbol **library_symbolspp,
Paul Gilliam76c61f12006-06-14 06:55:21 +020024 enum toplt type_of_plt, int is_weak);
Ian Wienand2d45b1a2006-02-20 22:48:07 +010025static int in_load_libraries(const char *name, struct ltelf *lte);
Ian Wienand4bfcedd2006-08-07 04:03:15 +020026static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020027
Paul Gilliambe320772006-04-24 22:06:23 +020028#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010029extern char *PLTs_initialized_by_here;
Paul Gilliambe320772006-04-24 22:06:23 +020030#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010031
Juan Cespedesf1350522008-12-16 18:19:58 +010032static void
33do_init_elf(struct ltelf *lte, const char *filename) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +010034 int i;
35 GElf_Addr relplt_addr = 0;
36 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +020037
Ian Wienand2d45b1a2006-02-20 22:48:07 +010038 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +020039
Ian Wienand2d45b1a2006-02-20 22:48:07 +010040 memset(lte, 0, sizeof(*lte));
41 lte->fd = open(filename, O_RDONLY);
42 if (lte->fd == -1)
43 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +020044
Juan Cespedesd914a202004-11-10 00:15:33 +010045#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand2d45b1a2006-02-20 22:48:07 +010046 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020047#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +010048 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020049#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +020050
Ian Wienand2d45b1a2006-02-20 22:48:07 +010051 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
52 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020053
Ian Wienand2d45b1a2006-02-20 22:48:07 +010054 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
55 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
56 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020057
Ian Wienand2d45b1a2006-02-20 22:48:07 +010058 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
59 error(EXIT_FAILURE, 0,
60 "\"%s\" is not an ELF executable nor shared library",
61 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020062
Ian Wienand2d45b1a2006-02-20 22:48:07 +010063 if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
64 || lte->ehdr.e_machine != LT_ELF_MACHINE)
Juan Cespedesd914a202004-11-10 00:15:33 +010065#ifdef LT_ELF_MACHINE2
Ian Wienand2d45b1a2006-02-20 22:48:07 +010066 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
67 || lte->ehdr.e_machine != LT_ELF_MACHINE2)
Juan Cespedesd914a202004-11-10 00:15:33 +010068#endif
69#ifdef LT_ELF_MACHINE3
Ian Wienand2d45b1a2006-02-20 22:48:07 +010070 && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3
71 || lte->ehdr.e_machine != LT_ELF_MACHINE3)
Juan Cespedesd914a202004-11-10 00:15:33 +010072#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +010073 )
74 error(EXIT_FAILURE, 0,
75 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010076
Ian Wienand2d45b1a2006-02-20 22:48:07 +010077 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
78 Elf_Scn *scn;
79 GElf_Shdr shdr;
80 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +010081
Ian Wienand2d45b1a2006-02-20 22:48:07 +010082 scn = elf_getscn(lte->elf, i);
83 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
84 error(EXIT_FAILURE, 0,
85 "Couldn't get section header from \"%s\"",
86 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010087
Ian Wienand2d45b1a2006-02-20 22:48:07 +010088 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
89 if (name == NULL)
90 error(EXIT_FAILURE, 0,
91 "Couldn't get section header from \"%s\"",
92 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010093
Ian Wienand2d45b1a2006-02-20 22:48:07 +010094 if (shdr.sh_type == SHT_SYMTAB) {
95 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +010096
Ian Wienand2d45b1a2006-02-20 22:48:07 +010097 lte->symtab = elf_getdata(scn, NULL);
98 lte->symtab_count = shdr.sh_size / shdr.sh_entsize;
99 if ((lte->symtab == NULL
100 || elf_getdata(scn, lte->symtab) != NULL)
101 && opt_x != NULL)
102 error(EXIT_FAILURE, 0,
103 "Couldn't get .symtab data from \"%s\"",
104 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100105
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100106 scn = elf_getscn(lte->elf, shdr.sh_link);
107 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
108 error(EXIT_FAILURE, 0,
109 "Couldn't get section header from \"%s\"",
110 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100111
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100112 data = elf_getdata(scn, NULL);
113 if (data == NULL || elf_getdata(scn, data) != NULL
114 || shdr.sh_size != data->d_size || data->d_off)
115 error(EXIT_FAILURE, 0,
116 "Couldn't get .strtab data from \"%s\"",
117 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100118
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100119 lte->strtab = data->d_buf;
120 } else if (shdr.sh_type == SHT_DYNSYM) {
121 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +0100122
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100123 lte->dynsym = elf_getdata(scn, NULL);
124 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
125 if (lte->dynsym == NULL
126 || elf_getdata(scn, lte->dynsym) != NULL)
127 error(EXIT_FAILURE, 0,
128 "Couldn't get .dynsym data from \"%s\"",
129 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100130
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100131 scn = elf_getscn(lte->elf, shdr.sh_link);
132 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
133 error(EXIT_FAILURE, 0,
134 "Couldn't get section header from \"%s\"",
135 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100136
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100137 data = elf_getdata(scn, NULL);
138 if (data == NULL || elf_getdata(scn, data) != NULL
139 || shdr.sh_size != data->d_size || data->d_off)
140 error(EXIT_FAILURE, 0,
141 "Couldn't get .dynstr data from \"%s\"",
142 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100143
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100144 lte->dynstr = data->d_buf;
145 } else if (shdr.sh_type == SHT_DYNAMIC) {
146 Elf_Data *data;
147 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100148
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100149 data = elf_getdata(scn, NULL);
150 if (data == NULL || elf_getdata(scn, data) != NULL)
151 error(EXIT_FAILURE, 0,
152 "Couldn't get .dynamic data from \"%s\"",
153 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100154
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100155 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
156 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100157
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100158 if (gelf_getdyn(data, j, &dyn) == NULL)
159 error(EXIT_FAILURE, 0,
160 "Couldn't get .dynamic data from \"%s\"",
161 filename);
Eric Vaitl1228a912006-12-28 16:16:56 +0100162#ifdef __mips__
163/**
164 MIPS ABI Supplement:
Ian Wienand9a2ad352006-02-20 22:44:45 +0100165
Juan Cespedesf1350522008-12-16 18:19:58 +0100166 DT_PLTGOT This member holds the address of the .got section.
Eric Vaitl1228a912006-12-28 16:16:56 +0100167
168 DT_MIPS_SYMTABNO This member holds the number of entries in the
169 .dynsym section.
170
171 DT_MIPS_LOCAL_GOTNO This member holds the number of local global
172 offset table entries.
173
174 DT_MIPS_GOTSYM This member holds the index of the first dyamic
175 symbol table entry that corresponds to an entry in the gobal offset
176 table.
177
178 */
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200179 if(dyn.d_tag==DT_PLTGOT){
Juan Cespedesf1350522008-12-16 18:19:58 +0100180 lte->pltgot_addr=dyn.d_un.d_ptr;
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200181 }
182 if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
183 lte->mips_local_gotno=dyn.d_un.d_val;
184 }
185 if(dyn.d_tag==DT_MIPS_GOTSYM){
186 lte->mips_gotsym=dyn.d_un.d_val;
187 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100188#endif // __mips__
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100189 if (dyn.d_tag == DT_JMPREL)
190 relplt_addr = dyn.d_un.d_ptr;
191 else if (dyn.d_tag == DT_PLTRELSZ)
192 relplt_size = dyn.d_un.d_val;
193 }
194 } else if (shdr.sh_type == SHT_HASH) {
195 Elf_Data *data;
196 size_t j;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100197
Petr Machata35fe5182006-07-18 12:58:12 +0200198 lte->hash_type = SHT_HASH;
199
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100200 data = elf_getdata(scn, NULL);
201 if (data == NULL || elf_getdata(scn, data) != NULL
202 || data->d_off || data->d_size != shdr.sh_size)
203 error(EXIT_FAILURE, 0,
204 "Couldn't get .hash data from \"%s\"",
205 filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100206
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100207 if (shdr.sh_entsize == 4) {
208 /* Standard conforming ELF. */
209 if (data->d_type != ELF_T_WORD)
210 error(EXIT_FAILURE, 0,
211 "Couldn't get .hash data from \"%s\"",
212 filename);
213 lte->hash = (Elf32_Word *) data->d_buf;
214 } else if (shdr.sh_entsize == 8) {
215 /* Alpha or s390x. */
216 Elf32_Word *dst, *src;
217 size_t hash_count = data->d_size / 8;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100218
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100219 lte->hash = (Elf32_Word *)
220 malloc(hash_count * sizeof(Elf32_Word));
221 if (lte->hash == NULL)
222 error(EXIT_FAILURE, 0,
223 "Couldn't convert .hash section from \"%s\"",
224 filename);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200225 lte->lte_flags |= LTE_HASH_MALLOCED;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100226 dst = lte->hash;
227 src = (Elf32_Word *) data->d_buf;
228 if ((data->d_type == ELF_T_WORD
229 && __BYTE_ORDER == __BIG_ENDIAN)
230 || (data->d_type == ELF_T_XWORD
231 && lte->ehdr.e_ident[EI_DATA] ==
232 ELFDATA2MSB))
233 ++src;
234 for (j = 0; j < hash_count; ++j, src += 2)
235 *dst++ = *src;
236 } else
237 error(EXIT_FAILURE, 0,
238 "Unknown .hash sh_entsize in \"%s\"",
239 filename);
Petr Machata35fe5182006-07-18 12:58:12 +0200240 } else if (shdr.sh_type == SHT_GNU_HASH
241 && lte->hash == NULL) {
242 Elf_Data *data;
Petr Machata35fe5182006-07-18 12:58:12 +0200243
244 lte->hash_type = SHT_GNU_HASH;
245
246 if (shdr.sh_entsize != 0
247 && shdr.sh_entsize != 4) {
248 error(EXIT_FAILURE, 0,
Olaf Hering03c087b2006-09-25 02:31:27 +0200249 ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu",
Petr Machata35fe5182006-07-18 12:58:12 +0200250 filename, shdr.sh_entsize);
251 }
252
253 data = elf_getdata(scn, NULL);
254 if (data == NULL || elf_getdata(scn, data) != NULL
255 || data->d_off || data->d_size != shdr.sh_size)
256 error(EXIT_FAILURE, 0,
257 "Couldn't get .gnu.hash data from \"%s\"",
258 filename);
259
260 lte->hash = (Elf32_Word *) data->d_buf;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100261 } else if (shdr.sh_type == SHT_PROGBITS
262 || shdr.sh_type == SHT_NOBITS) {
263 if (strcmp(name, ".plt") == 0) {
264 lte->plt_addr = shdr.sh_addr;
265 lte->plt_size = shdr.sh_size;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200266 if (shdr.sh_flags & SHF_EXECINSTR) {
267 lte->lte_flags |= LTE_PLT_EXECUTABLE;
268 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100269 }
270#ifdef ARCH_SUPPORTS_OPD
271 else if (strcmp(name, ".opd") == 0) {
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200272 lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100273 lte->opd_size = shdr.sh_size;
274 lte->opd = elf_rawdata(scn, NULL);
275 }
Petr Machatab3f8fef2006-11-30 14:45:07 +0100276#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100277 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100278 }
279
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100280 if (lte->dynsym == NULL || lte->dynstr == NULL)
281 error(EXIT_FAILURE, 0,
282 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100283
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100284 if (!relplt_addr || !lte->plt_addr) {
285 debug(1, "%s has no PLT relocations", filename);
286 lte->relplt = NULL;
287 lte->relplt_count = 0;
288 } else {
289 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
290 Elf_Scn *scn;
291 GElf_Shdr shdr;
292
293 scn = elf_getscn(lte->elf, i);
294 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
295 error(EXIT_FAILURE, 0,
296 "Couldn't get section header from \"%s\"",
297 filename);
298 if (shdr.sh_addr == relplt_addr
299 && shdr.sh_size == relplt_size) {
300 lte->relplt = elf_getdata(scn, NULL);
301 lte->relplt_count =
302 shdr.sh_size / shdr.sh_entsize;
303 if (lte->relplt == NULL
304 || elf_getdata(scn, lte->relplt) != NULL)
305 error(EXIT_FAILURE, 0,
306 "Couldn't get .rel*.plt data from \"%s\"",
307 filename);
308 break;
309 }
310 }
311
312 if (i == lte->ehdr.e_shnum)
313 error(EXIT_FAILURE, 0,
314 "Couldn't find .rel*.plt section in \"%s\"",
315 filename);
316
317 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
318 }
319}
320
Juan Cespedesf1350522008-12-16 18:19:58 +0100321static void
322do_close_elf(struct ltelf *lte) {
Paul Gilliam76c61f12006-06-14 06:55:21 +0200323 if (lte->lte_flags & LTE_HASH_MALLOCED)
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100324 free((char *)lte->hash);
325 elf_end(lte->elf);
326 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200327}
328
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100329static void
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100330add_library_symbol(GElf_Addr addr, const char *name,
331 struct library_symbol **library_symbolspp,
Juan Cespedesf1350522008-12-16 18:19:58 +0100332 enum toplt type_of_plt, int is_weak) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100333 struct library_symbol *s;
334 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
335 if (s == NULL)
336 error(EXIT_FAILURE, errno, "add_library_symbol failed");
337
338 s->needs_init = 1;
339 s->is_weak = is_weak;
Paul Gilliam76c61f12006-06-14 06:55:21 +0200340 s->plt_type = type_of_plt;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100341 s->next = *library_symbolspp;
342 s->enter_addr = (void *)(uintptr_t) addr;
343 s->brkpnt = NULL;
344 s->name = (char *)(s + 1);
345 strcpy(s->name, name);
346 *library_symbolspp = s;
347
348 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100349}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200350
Olaf Hering03c087b2006-09-25 02:31:27 +0200351/* stolen from elfutils-0.123 */
Juan Cespedesf1350522008-12-16 18:19:58 +0100352static unsigned long
353private_elf_gnu_hash(const char *name) {
Olaf Hering03c087b2006-09-25 02:31:27 +0200354 unsigned long h = 5381;
355 const unsigned char *string = (const unsigned char *)name;
356 unsigned char c;
357 for (c = *string; c; c = *++string)
358 h = h * 33 + c;
359 return h & 0xffffffff;
360}
361
Juan Cespedesf1350522008-12-16 18:19:58 +0100362static int
363in_load_libraries(const char *name, struct ltelf *lte) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100364 size_t i;
365 unsigned long hash;
Petr Machata35fe5182006-07-18 12:58:12 +0200366 unsigned long gnu_hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100367
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100368 if (!library_num)
369 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100370
Paul Gilliam3f1219f2006-04-24 18:25:38 +0200371 hash = elf_hash((const unsigned char *)name);
Petr Machata07bc4d12006-11-30 14:47:40 +0100372 gnu_hash = private_elf_gnu_hash(name);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100373 for (i = 1; i <= library_num; ++i) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100374 if (lte[i].hash == NULL)
375 continue;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200376
Petr Machata35fe5182006-07-18 12:58:12 +0200377 if (lte[i].hash_type == SHT_GNU_HASH) {
378 Elf32_Word * hashbase = lte[i].hash;
379 Elf32_Word nbuckets = *hashbase++;
380 Elf32_Word symbias = *hashbase++;
381 Elf32_Word bitmask_nwords = *hashbase++;
Petr Machata35fe5182006-07-18 12:58:12 +0200382 Elf32_Word * buckets;
383 Elf32_Word * chain_zero;
384 Elf32_Word bucket;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200385
Petr Machatab3f8fef2006-11-30 14:45:07 +0100386 // +1 for skipped `shift'
387 hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
Petr Machata35fe5182006-07-18 12:58:12 +0200388 buckets = hashbase;
389 hashbase += nbuckets;
390 chain_zero = hashbase - symbias;
391 bucket = buckets[gnu_hash % nbuckets];
Juan Cespedesd914a202004-11-10 00:15:33 +0100392
Petr Machata35fe5182006-07-18 12:58:12 +0200393 if (bucket != 0) {
394 const Elf32_Word *hasharr = &chain_zero[bucket];
395 do
396 if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
397 int symidx = hasharr - chain_zero;
398 GElf_Sym sym;
399
400 if (gelf_getsym(lte[i].dynsym, symidx, &sym) == NULL)
401 error(EXIT_FAILURE, 0,
402 "Couldn't get symbol from .dynsym");
403
404 if (sym.st_value != 0
405 && sym.st_shndx != SHN_UNDEF
406 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
407 return 1;
408 }
409 while ((*hasharr++ & 1u) == 0);
410 }
Olaf Hering03c087b2006-09-25 02:31:27 +0200411 } else {
Petr Machata35fe5182006-07-18 12:58:12 +0200412 Elf32_Word nbuckets, symndx;
413 Elf32_Word *buckets, *chain;
414 nbuckets = lte[i].hash[0];
415 buckets = &lte[i].hash[2];
416 chain = &lte[i].hash[2 + nbuckets];
417
418 for (symndx = buckets[hash % nbuckets];
419 symndx != STN_UNDEF; symndx = chain[symndx]) {
420 GElf_Sym sym;
421
422 if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
423 error(EXIT_FAILURE, 0,
424 "Couldn't get symbol from .dynsym");
425
426 if (sym.st_value != 0
427 && sym.st_shndx != SHN_UNDEF
428 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
429 return 1;
430 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100431 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200432 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100433 return 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100434}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200435
Juan Cespedesf1350522008-12-16 18:19:58 +0100436static GElf_Addr
437opd2addr(struct ltelf *lte, GElf_Addr addr) {
Petr Machatab3f8fef2006-11-30 14:45:07 +0100438#ifdef ARCH_SUPPORTS_OPD
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200439 unsigned long base, offset;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200440
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100441 if (!lte->opd)
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200442 return addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100443
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200444 base = (unsigned long)lte->opd->d_buf;
445 offset = (unsigned long)addr - (unsigned long)lte->opd_addr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100446 if (offset > lte->opd_size)
447 error(EXIT_FAILURE, 0, "static plt not in .opd");
448
Olaf Heringa841f652006-09-15 01:57:49 +0200449 return *(GElf_Addr*)(base + offset);
Petr Machatab3f8fef2006-11-30 14:45:07 +0100450#else //!ARCH_SUPPORTS_OPD
451 return addr;
452#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100453}
454
Juan Cespedesf1350522008-12-16 18:19:58 +0100455struct library_symbol *
456read_elf(struct process *proc) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100457 struct library_symbol *library_symbols = NULL;
458 struct ltelf lte[MAX_LIBRARY + 1];
459 size_t i;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100460 struct opt_x_t *xptr;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100461 struct library_symbol **lib_tail = NULL;
Paul Gilliam24e643a2006-03-13 18:43:13 +0100462 int exit_out = 0;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100463
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100464 elf_version(EV_CURRENT);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100465
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100466 do_init_elf(lte, proc->filename);
467 proc->e_machine = lte->ehdr.e_machine;
468 for (i = 0; i < library_num; ++i)
469 do_init_elf(&lte[i + 1], library[i]);
Eric Vaitl1228a912006-12-28 16:16:56 +0100470#ifdef __mips__
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200471 // MIPS doesn't use the PLT and the GOT entries get changed
Juan Cespedesf1350522008-12-16 18:19:58 +0100472 // on startup.
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200473 proc->need_to_reinitialize_breakpoints = 1;
474 for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
475 GElf_Sym sym;
476 const char *name;
477 GElf_Addr addr = arch_plt_sym_val(lte, i, 0);
478 if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
479 error(EXIT_FAILURE, 0,
480 "Couldn't get relocation from \"%s\"",
Juan Cespedesf1350522008-12-16 18:19:58 +0100481 proc->filename);
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200482 }
483 name=lte->dynstr+sym.st_name;
484 if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
485 debug(2,"sym %s not a function",name);
486 continue;
487 }
488 add_library_symbol(addr, name, &library_symbols, 0,
489 ELF64_ST_BIND(sym.st_info) != 0);
490 if (!lib_tail)
491 lib_tail = &(library_symbols->next);
492 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100493#else
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100494 for (i = 0; i < lte->relplt_count; ++i) {
495 GElf_Rel rel;
496 GElf_Rela rela;
497 GElf_Sym sym;
498 GElf_Addr addr;
499 void *ret;
500 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100501
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100502 if (lte->relplt->d_type == ELF_T_REL) {
503 ret = gelf_getrel(lte->relplt, i, &rel);
504 rela.r_offset = rel.r_offset;
505 rela.r_info = rel.r_info;
506 rela.r_addend = 0;
507 } else
508 ret = gelf_getrela(lte->relplt, i, &rela);
509
510 if (ret == NULL
511 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
512 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
513 &sym) == NULL)
514 error(EXIT_FAILURE, 0,
515 "Couldn't get relocation from \"%s\"",
516 proc->filename);
517
Paul Gilliambe320772006-04-24 22:06:23 +0200518#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100519 if (!sym.st_value && PLTs_initialized_by_here)
520 proc->need_to_reinitialize_breakpoints = 1;
Paul Gilliambe320772006-04-24 22:06:23 +0200521#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100522
523 name = lte->dynstr + sym.st_name;
524 if (in_load_libraries(name, lte)) {
525 addr = arch_plt_sym_val(lte, i, &rela);
Paul Gilliam76c61f12006-06-14 06:55:21 +0200526 add_library_symbol(addr, name, &library_symbols,
527 (PLTS_ARE_EXECUTABLE(lte)
528 ? LS_TOPLT_EXEC : LS_TOPLT_POINT),
Petr Machata7003fee2006-07-18 13:06:26 +0200529 ELF64_ST_BIND(sym.st_info) == STB_WEAK);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100530 if (!lib_tail)
531 lib_tail = &(library_symbols->next);
532 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100533 }
Eric Vaitl1228a912006-12-28 16:16:56 +0100534#endif // !__mips__
Paul Gilliambe320772006-04-24 22:06:23 +0200535#ifdef PLT_REINITALISATION_BP
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200536 struct opt_x_t *main_cheat;
537
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100538 if (proc->need_to_reinitialize_breakpoints) {
Paul Gilliambe320772006-04-24 22:06:23 +0200539 /* Add "PLTs_initialized_by_here" to opt_x list, if not
Juan Cespedesa413e5b2007-09-04 17:34:53 +0200540 already there. */
Paul Gilliam2b2507a2006-04-07 18:32:07 +0200541 main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100542 if (main_cheat == NULL)
Petr Machata7003fee2006-07-18 13:06:26 +0200543 error(EXIT_FAILURE, 0, "Couldn't allocate memory");
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100544 main_cheat->next = opt_x;
Paul Gilliambe320772006-04-24 22:06:23 +0200545 main_cheat->found = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100546 main_cheat->name = PLTs_initialized_by_here;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100547
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100548 for (xptr = opt_x; xptr; xptr = xptr->next)
549 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
550 && main_cheat) {
551 free(main_cheat);
552 main_cheat = NULL;
553 break;
554 }
555 if (main_cheat)
556 opt_x = main_cheat;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100557 }
Paul Gilliambe320772006-04-24 22:06:23 +0200558#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100559
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100560 for (i = 0; i < lte->symtab_count; ++i) {
561 GElf_Sym sym;
562 GElf_Addr addr;
563 const char *name;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100564
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100565 if (gelf_getsym(lte->symtab, i, &sym) == NULL)
566 error(EXIT_FAILURE, 0,
567 "Couldn't get symbol from \"%s\"",
568 proc->filename);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100569
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100570 name = lte->strtab + sym.st_name;
571 addr = sym.st_value;
572 if (!addr)
573 continue;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100574
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100575 for (xptr = opt_x; xptr; xptr = xptr->next)
576 if (xptr->name && strcmp(xptr->name, name) == 0) {
577 /* FIXME: Should be able to use &library_symbols as above. But
578 when you do, none of the real library symbols cause breaks. */
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200579 add_library_symbol(opd2addr(lte, addr),
Paul Gilliam76c61f12006-06-14 06:55:21 +0200580 name, lib_tail, LS_TOPLT_NONE, 0);
Paul Gilliam24e643a2006-03-13 18:43:13 +0100581 xptr->found = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100582 break;
583 }
584 }
585 for (xptr = opt_x; xptr; xptr = xptr->next)
Paul Gilliam24e643a2006-03-13 18:43:13 +0100586 if ( ! xptr->found) {
587 char *badthing = "WARNING";
Paul Gilliambe320772006-04-24 22:06:23 +0200588#ifdef PLT_REINITALISATION_BP
589 if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
590 if (lte->ehdr.e_entry) {
591 add_library_symbol (
Ian Wienand4bfcedd2006-08-07 04:03:15 +0200592 opd2addr (lte, lte->ehdr.e_entry),
Paul Gilliambe320772006-04-24 22:06:23 +0200593 PLTs_initialized_by_here,
594 lib_tail, 1, 0);
595 fprintf (stderr, "WARNING: Using e_ent"
596 "ry from elf header (%p) for "
597 "address of \"%s\"\n", (void*)
598 (long) lte->ehdr.e_entry,
599 PLTs_initialized_by_here);
600 continue;
601 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100602 badthing = "ERROR";
603 exit_out = 1;
604 }
Paul Gilliambe320772006-04-24 22:06:23 +0200605#endif
Paul Gilliam24e643a2006-03-13 18:43:13 +0100606 fprintf (stderr,
Paul Gilliambe320772006-04-24 22:06:23 +0200607 "%s: Couldn't find symbol \"%s\" in file \"%s"
608 "\"\n", badthing, xptr->name, proc->filename);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100609 }
Paul Gilliam24e643a2006-03-13 18:43:13 +0100610 if (exit_out) {
611 exit (1);
612 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100613
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100614 for (i = 0; i < library_num + 1; ++i)
615 do_close_elf(&lte[i]);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100616
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100617 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200618}