blob: 4f543d73d891e9398564ad1b561829f3732cbaf9 [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"
Juan Cespedes96935a91997-08-09 23:45:39 +020018
Ian Wienand3219f322006-02-16 06:00:00 +010019static void do_init_elf(struct ltelf *lte, const char *filename);
20static void do_close_elf(struct ltelf *lte);
21static void add_library_symbol(GElf_Addr addr, const char *name,
22 struct library_symbol **library_symbolspp);
23static int in_load_libraries(const char *name, struct ltelf *lte);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020024
Ian Wienand3219f322006-02-16 06:00:00 +010025static void do_init_elf(struct ltelf *lte, const char *filename)
Juan Cespedesd914a202004-11-10 00:15:33 +010026{
Ian Wienand3219f322006-02-16 06:00:00 +010027 int i;
28 GElf_Addr relplt_addr = 0;
29 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +020030
Ian Wienand3219f322006-02-16 06:00:00 +010031 debug(1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +020032
Ian Wienand3219f322006-02-16 06:00:00 +010033 memset(lte, 0, sizeof(*lte));
34 lte->fd = open(filename, O_RDONLY);
35 if (lte->fd == -1)
36 error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +020037
Juan Cespedesd914a202004-11-10 00:15:33 +010038#ifdef HAVE_ELF_C_READ_MMAP
Ian Wienand3219f322006-02-16 06:00:00 +010039 lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020040#else
Ian Wienand3219f322006-02-16 06:00:00 +010041 lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020042#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +020043
Ian Wienand3219f322006-02-16 06:00:00 +010044 if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF)
45 error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020046
Ian Wienand3219f322006-02-16 06:00:00 +010047 if (gelf_getehdr(lte->elf, &lte->ehdr) == NULL)
48 error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"",
49 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020050
Ian Wienand3219f322006-02-16 06:00:00 +010051 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
52 error(EXIT_FAILURE, 0,
53 "\"%s\" is not an ELF executable nor shared library",
54 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020055
Ian Wienand3219f322006-02-16 06:00:00 +010056 if (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
57 || (lte->ehdr.e_machine != LT_ELF_MACHINE
Juan Cespedesd914a202004-11-10 00:15:33 +010058#ifdef LT_ELF_MACHINE2
Ian Wienand3219f322006-02-16 06:00:00 +010059 && lte->ehdr.e_machine != LT_ELF_MACHINE2
Juan Cespedesd914a202004-11-10 00:15:33 +010060#endif
61#ifdef LT_ELF_MACHINE3
Ian Wienand3219f322006-02-16 06:00:00 +010062 && lte->ehdr.e_machine != LT_ELF_MACHINE3
Juan Cespedesd914a202004-11-10 00:15:33 +010063#endif
Ian Wienand3219f322006-02-16 06:00:00 +010064 ))
65 error(EXIT_FAILURE, 0,
66 "\"%s\" is ELF from incompatible architecture", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010067
Ian Wienand3219f322006-02-16 06:00:00 +010068 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
69 Elf_Scn *scn;
70 GElf_Shdr shdr;
71 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +010072
Ian Wienand3219f322006-02-16 06:00:00 +010073 scn = elf_getscn(lte->elf, i);
74 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
75 error(EXIT_FAILURE, 0,
76 "Couldn't get section header from \"%s\"",
77 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010078
Ian Wienand3219f322006-02-16 06:00:00 +010079 name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
80 if (name == NULL)
81 error(EXIT_FAILURE, 0,
82 "Couldn't get section header from \"%s\"",
83 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010084
Ian Wienand3219f322006-02-16 06:00:00 +010085 if (shdr.sh_type == SHT_DYNSYM) {
86 Elf_Data *data;
Juan Cespedesd914a202004-11-10 00:15:33 +010087
Ian Wienand3219f322006-02-16 06:00:00 +010088 lte->dynsym = elf_getdata(scn, NULL);
89 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
90 if (lte->dynsym == NULL
91 || elf_getdata(scn, lte->dynsym) != NULL)
92 error(EXIT_FAILURE, 0,
93 "Couldn't get .dynsym data from \"%s\"",
94 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +010095
Ian Wienand3219f322006-02-16 06:00:00 +010096 scn = elf_getscn(lte->elf, shdr.sh_link);
97 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
98 error(EXIT_FAILURE, 0,
99 "Couldn't get section header from \"%s\"",
100 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100101
Ian Wienand3219f322006-02-16 06:00:00 +0100102 data = elf_getdata(scn, NULL);
103 if (data == NULL || elf_getdata(scn, data) != NULL
104 || shdr.sh_size != data->d_size || data->d_off)
105 error(EXIT_FAILURE, 0,
106 "Couldn't get .dynstr data from \"%s\"",
107 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100108
Ian Wienand3219f322006-02-16 06:00:00 +0100109 lte->dynstr = data->d_buf;
110 } else if (shdr.sh_type == SHT_DYNAMIC) {
111 Elf_Data *data;
112 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100113
Ian Wienand3219f322006-02-16 06:00:00 +0100114 data = elf_getdata(scn, NULL);
115 if (data == NULL || elf_getdata(scn, data) != NULL)
116 error(EXIT_FAILURE, 0,
117 "Couldn't get .dynamic data from \"%s\"",
118 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100119
Ian Wienand3219f322006-02-16 06:00:00 +0100120 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
121 GElf_Dyn dyn;
Juan Cespedesd914a202004-11-10 00:15:33 +0100122
Ian Wienand3219f322006-02-16 06:00:00 +0100123 if (gelf_getdyn(data, j, &dyn) == NULL)
124 error(EXIT_FAILURE, 0,
125 "Couldn't get .dynamic data from \"%s\"",
126 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100127
Ian Wienand3219f322006-02-16 06:00:00 +0100128 if (dyn.d_tag == DT_JMPREL)
129 relplt_addr = dyn.d_un.d_ptr;
130 else if (dyn.d_tag == DT_PLTRELSZ)
131 relplt_size = dyn.d_un.d_val;
132 }
133 } else if (shdr.sh_type == SHT_HASH) {
134 Elf_Data *data;
135 size_t j;
Juan Cespedesd914a202004-11-10 00:15:33 +0100136
Ian Wienand3219f322006-02-16 06:00:00 +0100137 data = elf_getdata(scn, NULL);
138 if (data == NULL || elf_getdata(scn, data) != NULL
139 || data->d_off || data->d_size != shdr.sh_size)
140 error(EXIT_FAILURE, 0,
141 "Couldn't get .hash data from \"%s\"",
142 filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100143
Ian Wienand3219f322006-02-16 06:00:00 +0100144 if (shdr.sh_entsize == 4) {
145 /* Standard conforming ELF. */
146 if (data->d_type != ELF_T_WORD)
147 error(EXIT_FAILURE, 0,
148 "Couldn't get .hash data from \"%s\"",
149 filename);
150 lte->hash = (Elf32_Word *) data->d_buf;
151 } else if (shdr.sh_entsize == 8) {
152 /* Alpha or s390x. */
153 Elf32_Word *dst, *src;
154 size_t hash_count = data->d_size / 8;
Juan Cespedesd914a202004-11-10 00:15:33 +0100155
Ian Wienand3219f322006-02-16 06:00:00 +0100156 lte->hash = (Elf32_Word *)
157 malloc(hash_count * sizeof(Elf32_Word));
158 if (lte->hash == NULL)
159 error(EXIT_FAILURE, 0,
160 "Couldn't convert .hash section from \"%s\"",
161 filename);
162 lte->hash_malloced = 1;
163 dst = lte->hash;
164 src = (Elf32_Word *) data->d_buf;
165 if ((data->d_type == ELF_T_WORD
166 && __BYTE_ORDER == __BIG_ENDIAN)
167 || (data->d_type == ELF_T_XWORD
168 && lte->ehdr.e_ident[EI_DATA] ==
169 ELFDATA2MSB))
170 ++src;
171 for (j = 0; j < hash_count; ++j, src += 2)
172 *dst++ = *src;
173 } else
174 error(EXIT_FAILURE, 0,
175 "Unknown .hash sh_entsize in \"%s\"",
176 filename);
177 } else
178 if ((shdr.sh_type == SHT_PROGBITS
179 || shdr.sh_type == SHT_NOBITS)
180 && strcmp(name, ".plt") == 0)
181 lte->plt_addr = shdr.sh_addr;
Juan Cespedesd914a202004-11-10 00:15:33 +0100182 }
183
Ian Wienand3219f322006-02-16 06:00:00 +0100184 if (lte->dynsym == NULL || lte->dynstr == NULL)
185 error(EXIT_FAILURE, 0,
186 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100187
Ian Wienand3219f322006-02-16 06:00:00 +0100188 if (!relplt_addr || !lte->plt_addr) {
189 debug(1, "%s has no PLT relocations", filename);
190 lte->relplt = NULL;
191 lte->relplt_count = 0;
192 } else {
193 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
194 Elf_Scn *scn;
195 GElf_Shdr shdr;
196
197 scn = elf_getscn(lte->elf, i);
198 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
199 error(EXIT_FAILURE, 0,
200 "Couldn't get section header from \"%s\"",
201 filename);
202 if (shdr.sh_addr == relplt_addr
203 && shdr.sh_size == relplt_size) {
204 lte->relplt = elf_getdata(scn, NULL);
205 lte->relplt_count =
206 shdr.sh_size / shdr.sh_entsize;
207 if (lte->relplt == NULL
208 || elf_getdata(scn, lte->relplt) != NULL)
209 error(EXIT_FAILURE, 0,
210 "Couldn't get .rel*.plt data from \"%s\"",
211 filename);
212 break;
213 }
214 }
215
216 if (i == lte->ehdr.e_shnum)
217 error(EXIT_FAILURE, 0,
218 "Couldn't find .rel*.plt section in \"%s\"",
219 filename);
220
221 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
222 }
223}
224
225static void do_close_elf(struct ltelf *lte)
226{
227 if (lte->hash_malloced)
228 free((char *)lte->hash);
229 elf_end(lte->elf);
230 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200231}
232
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100233static void
Ian Wienand3219f322006-02-16 06:00:00 +0100234add_library_symbol(GElf_Addr addr, const char *name,
235 struct library_symbol **library_symbolspp)
Juan Cespedesd914a202004-11-10 00:15:33 +0100236{
Ian Wienand3219f322006-02-16 06:00:00 +0100237 struct library_symbol *s;
238 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
239 if (s == NULL)
240 error(EXIT_FAILURE, errno, "add_library_symbol failed");
241
242 s->next = *library_symbolspp;
243 s->enter_addr = (void *)(uintptr_t) addr;
244 s->name = (char *)(s + 1);
245 strcpy(s->name, name);
246 *library_symbolspp = s;
247
248 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100249}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200250
Ian Wienand3219f322006-02-16 06:00:00 +0100251static int in_load_libraries(const char *name, struct ltelf *lte)
Juan Cespedesd914a202004-11-10 00:15:33 +0100252{
Ian Wienand3219f322006-02-16 06:00:00 +0100253 size_t i;
254 unsigned long hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100255
Ian Wienand3219f322006-02-16 06:00:00 +0100256 if (!library_num)
257 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100258
Ian Wienand3219f322006-02-16 06:00:00 +0100259 hash = elf_hash(name);
260 for (i = 1; i <= library_num; ++i) {
261 Elf32_Word nbuckets, symndx;
262 Elf32_Word *buckets, *chain;
263
264 if (lte[i].hash == NULL)
265 continue;
266
267 nbuckets = lte[i].hash[0];
268 buckets = &lte[i].hash[2];
269 chain = &lte[i].hash[2 + nbuckets];
270 for (symndx = buckets[hash % nbuckets];
271 symndx != STN_UNDEF; symndx = chain[symndx]) {
272 GElf_Sym sym;
273
274 if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
275 error(EXIT_FAILURE, 0,
276 "Couldn't get symbol from .dynsym");
277
278 if (sym.st_value != 0
279 && sym.st_shndx != SHN_UNDEF
280 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
281 return 1;
282 }
283 }
284 return 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200285}
286
Ian Wienand3219f322006-02-16 06:00:00 +0100287struct library_symbol *read_elf(const char *filename)
Juan Cespedesd914a202004-11-10 00:15:33 +0100288{
Ian Wienand3219f322006-02-16 06:00:00 +0100289 struct library_symbol *library_symbols = NULL;
290 struct ltelf lte[MAX_LIBRARY + 1];
291 size_t i;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200292
Ian Wienand3219f322006-02-16 06:00:00 +0100293 elf_version(EV_CURRENT);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200294
Ian Wienand3219f322006-02-16 06:00:00 +0100295 do_init_elf(lte, filename);
296 for (i = 0; i < library_num; ++i)
297 do_init_elf(&lte[i + 1], library[i]);
Juan Cespedesd914a202004-11-10 00:15:33 +0100298
Ian Wienand3219f322006-02-16 06:00:00 +0100299 for (i = 0; i < lte->relplt_count; ++i) {
300 GElf_Rel rel;
301 GElf_Rela rela;
302 GElf_Sym sym;
303 GElf_Addr addr;
304 void *ret;
305 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +0100306
Ian Wienand3219f322006-02-16 06:00:00 +0100307 if (lte->relplt->d_type == ELF_T_REL) {
308 ret = gelf_getrel(lte->relplt, i, &rel);
309 rela.r_offset = rel.r_offset;
310 rela.r_info = rel.r_info;
311 rela.r_addend = 0;
312 } else
313 ret = gelf_getrela(lte->relplt, i, &rela);
Juan Cespedesd914a202004-11-10 00:15:33 +0100314
Ian Wienand3219f322006-02-16 06:00:00 +0100315 if (ret == NULL
316 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
317 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
318 &sym) == NULL)
319 error(EXIT_FAILURE, 0,
320 "Couldn't get relocation from \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100321
Ian Wienand3219f322006-02-16 06:00:00 +0100322 name = lte->dynstr + sym.st_name;
323 if (in_load_libraries(name, lte)) {
324 addr = arch_plt_sym_val(lte, i, &rela);
325 if (addr != 0)
326 add_library_symbol(addr, name,
327 &library_symbols);
328 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200329 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200330
Ian Wienand3219f322006-02-16 06:00:00 +0100331 for (i = 0; i < library_num + 1; ++i)
332 do_close_elf(&lte[i]);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200333
Ian Wienand3219f322006-02-16 06:00:00 +0100334 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200335}