blob: 1d37eb99a107d35138f602131f620a2aae45e720 [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
Juan Cespedesd914a202004-11-10 00:15:33 +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
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +010025static void
Juan Cespedesd914a202004-11-10 00:15:33 +010026do_init_elf (struct ltelf *lte, const char *filename)
27{
28 int i;
29 GElf_Addr relplt_addr = 0;
30 size_t relplt_size = 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +020031
Juan Cespedesd914a202004-11-10 00:15:33 +010032 debug (1, "Reading ELF from %s...", filename);
Juan Cespedes1afec691997-08-23 21:31:46 +020033
Juan Cespedesd914a202004-11-10 00:15:33 +010034 memset (lte, 0, sizeof (*lte));
35 lte->fd = open (filename, O_RDONLY);
36 if (lte->fd == -1)
37 error (EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
Juan Cespedes96935a91997-08-09 23:45:39 +020038
Juan Cespedesd914a202004-11-10 00:15:33 +010039#ifdef HAVE_ELF_C_READ_MMAP
40 lte->elf = elf_begin (lte->fd, ELF_C_READ_MMAP, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020041#else
Juan Cespedesd914a202004-11-10 00:15:33 +010042 lte->elf = elf_begin (lte->fd, ELF_C_READ, NULL);
Juan Cespedes5c3fe062004-06-14 18:08:37 +020043#endif
Juan Cespedes1cd999a2001-07-03 00:46:04 +020044
Juan Cespedesd914a202004-11-10 00:15:33 +010045 if (lte->elf == NULL || elf_kind (lte->elf) != ELF_K_ELF)
46 error (EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020047
Juan Cespedesd914a202004-11-10 00:15:33 +010048 if (gelf_getehdr (lte->elf, &lte->ehdr) == NULL)
49 error (EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"", filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020050
Juan Cespedesd914a202004-11-10 00:15:33 +010051 if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN)
52 error (EXIT_FAILURE, 0, "\"%s\" is not an ELF executable nor shared library",
53 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +020054
Juan Cespedesd914a202004-11-10 00:15:33 +010055 if (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
56 || (lte->ehdr.e_machine != LT_ELF_MACHINE
57#ifdef LT_ELF_MACHINE2
58 && lte->ehdr.e_machine != LT_ELF_MACHINE2
59#endif
60#ifdef LT_ELF_MACHINE3
61 && lte->ehdr.e_machine != LT_ELF_MACHINE3
62#endif
63 ))
64 error (EXIT_FAILURE, 0, "\"%s\" is ELF from incompatible architecture",
65 filename);
66
67 for (i = 1; i < lte->ehdr.e_shnum; ++i)
68 {
69 Elf_Scn *scn;
70 GElf_Shdr shdr;
71 const char *name;
72
73 scn = elf_getscn (lte->elf, i);
74 if (scn == NULL || gelf_getshdr (scn, &shdr) == NULL)
75 error (EXIT_FAILURE, 0, "Couldn't get section header from \"%s\"",
76 filename);
77
78 name = elf_strptr (lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name);
79 if (name == NULL)
80 error (EXIT_FAILURE, 0, "Couldn't get section header from \"%s\"",
81 filename);
82
83 if (shdr.sh_type == SHT_DYNSYM)
84 {
85 Elf_Data *data;
86
87 lte->dynsym = elf_getdata (scn, NULL);
88 lte->dynsym_count = shdr.sh_size / shdr.sh_entsize;
89 if (lte->dynsym == NULL || elf_getdata (scn, lte->dynsym) != NULL)
90 error (EXIT_FAILURE, 0, "Couldn't get .dynsym data from \"%s\"",
91 filename);
92
93 scn = elf_getscn (lte->elf, shdr.sh_link);
94 if (scn == NULL || gelf_getshdr (scn, &shdr) == NULL)
95 error (EXIT_FAILURE, 0, "Couldn't get section header from \"%s\"",
96 filename);
97
98 data = elf_getdata (scn, NULL);
99 if (data == NULL || elf_getdata (scn, data) != NULL
100 || shdr.sh_size != data->d_size || data->d_off)
101 error (EXIT_FAILURE, 0, "Couldn't get .dynstr data from \"%s\"",
102 filename);
103
104 lte->dynstr = data->d_buf;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200105 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100106 else if (shdr.sh_type == SHT_DYNAMIC)
107 {
108 Elf_Data *data;
109 size_t j;
110
111 data = elf_getdata (scn, NULL);
112 if (data == NULL || elf_getdata (scn, data) != NULL)
113 error (EXIT_FAILURE, 0, "Couldn't get .dynamic data from \"%s\"",
114 filename);
115
116 for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j)
117 {
118 GElf_Dyn dyn;
119
120 if (gelf_getdyn (data, j, &dyn) == NULL)
121 error (EXIT_FAILURE, 0, "Couldn't get .dynamic data from \"%s\"",
122 filename);
123
124 if (dyn.d_tag == DT_JMPREL)
125 relplt_addr = dyn.d_un.d_ptr;
126 else if (dyn.d_tag == DT_PLTRELSZ)
127 relplt_size = dyn.d_un.d_val;
128 }
129 }
130 else if (shdr.sh_type == SHT_HASH)
131 {
132 Elf_Data *data;
133 size_t j;
134
135 data = elf_getdata (scn, NULL);
136 if (data == NULL || elf_getdata (scn, data) != NULL
137 || data->d_off || data->d_size != shdr.sh_size)
138 error (EXIT_FAILURE, 0, "Couldn't get .hash data from \"%s\"",
139 filename);
140
141 if (shdr.sh_entsize == 4)
142 {
143 /* Standard conforming ELF. */
144 if (data->d_type != ELF_T_WORD)
145 error (EXIT_FAILURE, 0, "Couldn't get .hash data from \"%s\"",
146 filename);
147 lte->hash = (Elf32_Word *) data->d_buf;
148 }
149 else if (shdr.sh_entsize == 8)
150 {
151 /* Alpha or s390x. */
152 Elf32_Word *dst, *src;
153 size_t hash_count = data->d_size / 8;
154
155 lte->hash = (Elf32_Word *)
156 malloc (hash_count * sizeof (Elf32_Word));
157 if (lte->hash == NULL)
158 error (EXIT_FAILURE, 0, "Couldn't convert .hash section from \"%s\"",
159 filename);
160 lte->hash_malloced = 1;
161 dst = lte->hash;
162 src = (Elf32_Word *) data->d_buf;
163 if ((data->d_type == ELF_T_WORD && __BYTE_ORDER == __BIG_ENDIAN)
164 || (data->d_type == ELF_T_XWORD
165 && lte->ehdr.e_ident[EI_DATA] == ELFDATA2MSB))
166 ++src;
167 for (j = 0; j < hash_count; ++j, src += 2)
168 *dst++ = *src;
169 }
170 else
171 error (EXIT_FAILURE, 0, "Unknown .hash sh_entsize in \"%s\"",
172 filename);
173 }
174 else if ((shdr.sh_type == SHT_PROGBITS || shdr.sh_type == SHT_NOBITS)
175 && strcmp (name, ".plt") == 0)
176 lte->plt_addr = shdr.sh_addr;
177 }
178
179 if (lte->dynsym == NULL || lte->dynstr == NULL)
180 error (EXIT_FAILURE, 0, "Couldn't find .dynsym or .dynstr in \"%s\"",
181 filename);
182
183 if (!relplt_addr || !lte->plt_addr)
184 {
185 debug (1, "%s has no PLT relocations", filename);
186 lte->relplt = NULL;
187 lte->relplt_count = 0;
188 }
189 else
190 {
191 for (i = 1; i < lte->ehdr.e_shnum; ++i)
192 {
193 Elf_Scn *scn;
194 GElf_Shdr shdr;
195
196 scn = elf_getscn (lte->elf, i);
197 if (scn == NULL || gelf_getshdr (scn, &shdr) == NULL)
198 error (EXIT_FAILURE, 0, "Couldn't get section header from \"%s\"",
199 filename);
200 if (shdr.sh_addr == relplt_addr && shdr.sh_size == relplt_size)
201 {
202 lte->relplt = elf_getdata (scn, NULL);
203 lte->relplt_count = shdr.sh_size / shdr.sh_entsize;
204 if (lte->relplt == NULL
205 || elf_getdata (scn, lte->relplt) != NULL)
206 error (EXIT_FAILURE, 0, "Couldn't get .rel*.plt data from \"%s\"",
207 filename);
208 break;
209 }
210 }
211
212 if (i == lte->ehdr.e_shnum)
213 error (EXIT_FAILURE, 0, "Couldn't find .rel*.plt section in \"%s\"",
214 filename);
215
216 debug (1, "%s %zd PLT relocations", filename, lte->relplt_count);
217 }
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200218}
219
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100220static void
Juan Cespedesd914a202004-11-10 00:15:33 +0100221do_close_elf (struct ltelf *lte)
222{
223 if (lte->hash_malloced)
224 free ((char *) lte->hash);
225 elf_end (lte->elf);
226 close (lte->fd);
227}
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200228
Juan Cespedesd914a202004-11-10 00:15:33 +0100229static void
230add_library_symbol (GElf_Addr addr, const char *name,
231 struct library_symbol **library_symbolspp)
232{
233 struct library_symbol *s;
234 s = malloc (sizeof (struct library_symbol) + strlen (name) + 1);
235 if (s == NULL)
236 error (EXIT_FAILURE, errno, "add_library_symbol failed");
237
238 s->next = *library_symbolspp;
239 s->enter_addr = (void *) (uintptr_t) addr;
240 s->name = (char *) (s + 1);
241 strcpy (s->name, name);
242 *library_symbolspp = s;
243
244 debug (2, "addr: %p, symbol: \"%s\"", (void *) (uintptr_t) addr, name);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200245}
246
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100247static int
Juan Cespedesd914a202004-11-10 00:15:33 +0100248in_load_libraries (const char *name, struct ltelf *lte)
249{
250 size_t i;
251 unsigned long hash;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200252
Juan Cespedesd914a202004-11-10 00:15:33 +0100253 if (!library_num)
254 return 1;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200255
Juan Cespedesd914a202004-11-10 00:15:33 +0100256 hash = elf_hash (name);
257 for (i = 1; i <= library_num; ++i)
258 {
259 Elf32_Word nbuckets, symndx;
260 Elf32_Word *buckets, *chain;
261
262 if (lte[i].hash == NULL)
263 continue;
264
265 nbuckets = lte[i].hash[0];
266 buckets = &lte[i].hash[2];
267 chain = &lte[i].hash[2 + nbuckets];
268 for (symndx = buckets[hash % nbuckets];
269 symndx != STN_UNDEF;
270 symndx = chain[symndx])
271 {
272 GElf_Sym sym;
273
274 if (gelf_getsym (lte[i].dynsym, symndx, &sym) == NULL)
275 error (EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym");
276
277 if (sym.st_value != 0
278 && sym.st_shndx != SHN_UNDEF
279 && strcmp (name, lte[i].dynstr + sym.st_name) == 0)
280 return 1;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200281 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100282 }
283 return 0;
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200284}
285
Juan Cespedes8cc1b9d2002-03-01 19:54:23 +0100286struct library_symbol *
Juan Cespedesd914a202004-11-10 00:15:33 +0100287read_elf (const char *filename)
288{
289 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
Juan Cespedesd914a202004-11-10 00:15:33 +0100293 elf_version (EV_CURRENT);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200294
Juan Cespedesd914a202004-11-10 00:15:33 +0100295 do_init_elf (lte, filename);
296 for (i = 0; i < library_num; ++i)
297 do_init_elf (&lte[i + 1], library[i]);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200298
Juan Cespedesd914a202004-11-10 00:15:33 +0100299 for (i = 0; i < lte->relplt_count; ++i)
300 {
301 GElf_Rel rel;
302 GElf_Rela rela;
303 GElf_Sym sym;
304 GElf_Addr addr;
305 void *ret;
306 const char *name;
307
308 if (lte->relplt->d_type == ELF_T_REL)
309 {
310 ret = gelf_getrel (lte->relplt, i, &rel);
311 rela.r_offset = rel.r_offset;
312 rela.r_info = rel.r_info;
313 rela.r_addend = 0;
Juan Cespedes96935a91997-08-09 23:45:39 +0200314 }
Juan Cespedesd914a202004-11-10 00:15:33 +0100315 else
316 ret = gelf_getrela (lte->relplt, i, &rela);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200317
Juan Cespedesd914a202004-11-10 00:15:33 +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), &sym) == NULL)
321 error (EXIT_FAILURE, 0, "Couldn't get relocation from \"%s\"",
322 filename);
Juan Cespedes1cd999a2001-07-03 00:46:04 +0200323
Juan Cespedesd914a202004-11-10 00:15:33 +0100324 name = lte->dynstr + sym.st_name;
325 if (in_load_libraries (name, lte))
326 {
327 addr = arch_plt_sym_val (lte, i, &rela);
328 if (addr != 0)
329 add_library_symbol (addr, name, &library_symbols);
330 }
331 }
332
333 for (i = 0; i < library_num + 1; ++i)
334 do_close_elf (&lte[i]);
335
336 return library_symbols;
Juan Cespedes96935a91997-08-09 23:45:39 +0200337}