blob: db5378c5c427a54ce113b5b3728891ed2b52b3d6 [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)
Ian Wienand5570a772006-02-17 02:00:00 +0100181 {
Ian Wienand3219f322006-02-16 06:00:00 +0100182 lte->plt_addr = shdr.sh_addr;
Ian Wienand5570a772006-02-17 02:00:00 +0100183 lte->plt_size = shdr.sh_size;
184 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100185 }
186
Ian Wienand3219f322006-02-16 06:00:00 +0100187 if (lte->dynsym == NULL || lte->dynstr == NULL)
188 error(EXIT_FAILURE, 0,
189 "Couldn't find .dynsym or .dynstr in \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100190
Ian Wienand3219f322006-02-16 06:00:00 +0100191 if (!relplt_addr || !lte->plt_addr) {
192 debug(1, "%s has no PLT relocations", filename);
193 lte->relplt = NULL;
194 lte->relplt_count = 0;
195 } else {
196 for (i = 1; i < lte->ehdr.e_shnum; ++i) {
197 Elf_Scn *scn;
198 GElf_Shdr shdr;
199
200 scn = elf_getscn(lte->elf, i);
201 if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
202 error(EXIT_FAILURE, 0,
203 "Couldn't get section header from \"%s\"",
204 filename);
205 if (shdr.sh_addr == relplt_addr
206 && shdr.sh_size == relplt_size) {
207 lte->relplt = elf_getdata(scn, NULL);
208 lte->relplt_count =
209 shdr.sh_size / shdr.sh_entsize;
210 if (lte->relplt == NULL
211 || elf_getdata(scn, lte->relplt) != NULL)
212 error(EXIT_FAILURE, 0,
213 "Couldn't get .rel*.plt data from \"%s\"",
214 filename);
215 break;
216 }
217 }
218
219 if (i == lte->ehdr.e_shnum)
220 error(EXIT_FAILURE, 0,
221 "Couldn't find .rel*.plt section in \"%s\"",
222 filename);
223
224 debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
225 }
226}
227
228static void do_close_elf(struct ltelf *lte)
229{
230 if (lte->hash_malloced)
231 free((char *)lte->hash);
232 elf_end(lte->elf);
233 close(lte->fd);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200234}
235
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100236static void
Ian Wienand3219f322006-02-16 06:00:00 +0100237add_library_symbol(GElf_Addr addr, const char *name,
238 struct library_symbol **library_symbolspp)
Juan Cespedesd914a202004-11-10 00:15:33 +0100239{
Ian Wienand3219f322006-02-16 06:00:00 +0100240 struct library_symbol *s;
241 s = malloc(sizeof(struct library_symbol) + strlen(name) + 1);
242 if (s == NULL)
243 error(EXIT_FAILURE, errno, "add_library_symbol failed");
244
245 s->next = *library_symbolspp;
246 s->enter_addr = (void *)(uintptr_t) addr;
247 s->name = (char *)(s + 1);
248 strcpy(s->name, name);
249 *library_symbolspp = s;
250
251 debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
Juan Cespedesd914a202004-11-10 00:15:33 +0100252}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200253
Ian Wienand3219f322006-02-16 06:00:00 +0100254static int in_load_libraries(const char *name, struct ltelf *lte)
Juan Cespedesd914a202004-11-10 00:15:33 +0100255{
Ian Wienand3219f322006-02-16 06:00:00 +0100256 size_t i;
257 unsigned long hash;
Juan Cespedesd914a202004-11-10 00:15:33 +0100258
Ian Wienand3219f322006-02-16 06:00:00 +0100259 if (!library_num)
260 return 1;
Juan Cespedesd914a202004-11-10 00:15:33 +0100261
Ian Wienand3219f322006-02-16 06:00:00 +0100262 hash = elf_hash(name);
263 for (i = 1; i <= library_num; ++i) {
264 Elf32_Word nbuckets, symndx;
265 Elf32_Word *buckets, *chain;
266
267 if (lte[i].hash == NULL)
268 continue;
269
270 nbuckets = lte[i].hash[0];
271 buckets = &lte[i].hash[2];
272 chain = &lte[i].hash[2 + nbuckets];
273 for (symndx = buckets[hash % nbuckets];
274 symndx != STN_UNDEF; symndx = chain[symndx]) {
275 GElf_Sym sym;
276
277 if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
278 error(EXIT_FAILURE, 0,
279 "Couldn't get symbol from .dynsym");
280
281 if (sym.st_value != 0
282 && sym.st_shndx != SHN_UNDEF
283 && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
284 return 1;
285 }
286 }
287 return 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200288}
289
Ian Wienand3219f322006-02-16 06:00:00 +0100290struct library_symbol *read_elf(const char *filename)
Juan Cespedesd914a202004-11-10 00:15:33 +0100291{
Ian Wienand3219f322006-02-16 06:00:00 +0100292 struct library_symbol *library_symbols = NULL;
293 struct ltelf lte[MAX_LIBRARY + 1];
294 size_t i;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200295
Ian Wienand3219f322006-02-16 06:00:00 +0100296 elf_version(EV_CURRENT);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200297
Ian Wienand3219f322006-02-16 06:00:00 +0100298 do_init_elf(lte, filename);
299 for (i = 0; i < library_num; ++i)
300 do_init_elf(&lte[i + 1], library[i]);
Juan Cespedesd914a202004-11-10 00:15:33 +0100301
Ian Wienand3219f322006-02-16 06:00:00 +0100302 for (i = 0; i < lte->relplt_count; ++i) {
303 GElf_Rel rel;
304 GElf_Rela rela;
305 GElf_Sym sym;
306 GElf_Addr addr;
307 void *ret;
308 const char *name;
Juan Cespedesd914a202004-11-10 00:15:33 +0100309
Ian Wienand3219f322006-02-16 06:00:00 +0100310 if (lte->relplt->d_type == ELF_T_REL) {
311 ret = gelf_getrel(lte->relplt, i, &rel);
312 rela.r_offset = rel.r_offset;
313 rela.r_info = rel.r_info;
314 rela.r_addend = 0;
315 } else
316 ret = gelf_getrela(lte->relplt, i, &rela);
Juan Cespedesd914a202004-11-10 00:15:33 +0100317
Ian Wienand3219f322006-02-16 06:00:00 +0100318 if (ret == NULL
319 || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
320 || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
321 &sym) == NULL)
322 error(EXIT_FAILURE, 0,
323 "Couldn't get relocation from \"%s\"", filename);
Juan Cespedesd914a202004-11-10 00:15:33 +0100324
Ian Wienand3219f322006-02-16 06:00:00 +0100325 name = lte->dynstr + sym.st_name;
326 if (in_load_libraries(name, lte)) {
327 addr = arch_plt_sym_val(lte, i, &rela);
328 if (addr != 0)
329 add_library_symbol(addr, name,
330 &library_symbols);
331 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200332 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200333
Ian Wienand3219f322006-02-16 06:00:00 +0100334 for (i = 0; i < library_num + 1; ++i)
335 do_close_elf(&lte[i]);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200336
Ian Wienand3219f322006-02-16 06:00:00 +0100337 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200338}