1/*
2Copyright (c) 2003-2006 Hewlett-Packard Development Company, L.P.
3Permission is hereby granted, free of charge, to any person
4obtaining a copy of this software and associated documentation
5files (the "Software"), to deal in the Software without
6restriction, including without limitation the rights to use,
7copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following
10conditions:
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23*/
24
25#ifdef USE_CLEAN_NAMESPACE
26#define fopen _fopen
27#define fseek _fseek
28#define fread _fread
29#define fclose _fclose
30#endif /* USE_CLEAN_NAMESPACE */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <inttypes.h>
36#include <elf.h>
37
38#include "uwx.h"
39#include "uwx_env.h"
40
41#ifdef USE_CLEAN_NAMESPACE
42/*
43 * Moved the defines above the include of stdio.h,
44 * so we don't need these unless that causes problems
45 * and we have to move them back down here.
46 * #define fopen _fopen
47 * #define fseek _fseek
48 * #define fread _fread
49 * #define fclose _fclose
50 * extern FILE *_fopen(const char *, const char *);
51 * extern int _fseek(FILE *, long int, int);
52 * extern size_t _fread(void *, size_t, size_t, FILE *);
53 * extern int _fclose(FILE *);
54 */
55#endif /* USE_CLEAN_NAMESPACE */
56
57struct uwx_symbol_cache {
58    char *module_name;
59    int nsyms;
60    uint64_t *sym_values;
61    char **sym_names;
62    char *strings;
63};
64
65
66int uwx_read_func_symbols(
67    struct uwx_env *env,
68    struct uwx_symbol_cache *cache,
69    char *module_name);
70
71
72int uwx_find_symbol(
73    struct uwx_env *env,
74    struct uwx_symbol_cache **symbol_cache_p,
75    char *module_name,
76    uint64_t relip,
77    char **func_name_p,
78    uint64_t *offset_p)
79{
80    int status;
81    int i;
82    uint64_t offset;
83    uint64_t best_offset;
84    char *best_name;
85    struct symbol *sym;
86    struct uwx_symbol_cache *cache = NULL;
87
88    /* Allocate a symbol cache on first call */
89    if (symbol_cache_p != NULL)
90	cache = *symbol_cache_p;
91    if (cache == NULL) {
92	cache = (struct uwx_symbol_cache *)
93			(*env->allocate_cb)(sizeof(struct uwx_symbol_cache));
94	if (cache == NULL)
95	    return UWX_ERR_NOMEM;
96	cache->module_name = NULL;
97	cache->nsyms = 0;
98	cache->sym_values = NULL;
99	cache->sym_names = NULL;
100	cache->strings = NULL;
101	if (symbol_cache_p != NULL)
102	    *symbol_cache_p = cache;
103    }
104
105    /* Read function symbols from the object file */
106    status = uwx_read_func_symbols(env, cache, module_name);
107    if (status != UWX_OK)
108	return status;
109
110    /* Search for best match */
111    best_offset = ~(uint64_t)0;
112    best_name = NULL;
113    for (i = 0; i < cache->nsyms; i++) {
114	if (cache->sym_values[i] == relip) {
115	    *func_name_p = cache->sym_names[i];
116	    *offset_p = 0;
117	    if (symbol_cache_p == NULL)
118		uwx_release_symbol_cache(env, cache);
119	    return UWX_OK;
120	}
121	if (relip > cache->sym_values[i]) {
122	    offset = relip - cache->sym_values[i];
123	    if (offset < best_offset) {
124		best_offset = offset;
125		best_name = cache->sym_names[i];
126	    }
127	}
128    }
129    if (best_name == NULL)
130	return UWX_ERR_NOSYM;
131
132    if (symbol_cache_p == NULL)
133	uwx_release_symbol_cache(env, cache);
134
135    *func_name_p = best_name;
136    *offset_p = best_offset;
137    return UWX_OK;
138}
139
140
141void uwx_release_symbol_cache(
142    struct uwx_env *env,
143    struct uwx_symbol_cache *symbol_cache)
144{
145    if (symbol_cache->module_name != NULL)
146	(*env->free_cb)(symbol_cache->module_name);
147    if (symbol_cache->sym_values != NULL)
148	(*env->free_cb)(symbol_cache->sym_values);
149    if (symbol_cache->sym_names != NULL)
150	(*env->free_cb)(symbol_cache->sym_names);
151    if (symbol_cache->strings != NULL)
152	(*env->free_cb)(symbol_cache->strings);
153    (*env->free_cb)(symbol_cache);
154}
155
156
157#define ELF_ERR_NOMEM		UWX_ERR_NOMEM  /* Out of memory */
158#define ELF_ERR_OPEN		UWX_ERR_NOSYM  /* Can't open file */
159
160#define ELF_ERR_NOHEADER	UWX_ERR_NOSYM  /* Can't read ELF header */
161#define ELF_ERR_NOTELF		UWX_ERR_NOSYM  /* Not an ELF file */
162#define ELF_ERR_HEADER_SIZE	UWX_ERR_NOSYM  /* Invalid e_ehsize */
163#define ELF_ERR_INVALID_CLASS	UWX_ERR_NOSYM  /* Invalid EI_CLASS */
164#define ELF_ERR_INVALID_DATA	UWX_ERR_NOSYM  /* Invalid EI_DATA */
165
166#define ELF_ERR_READ_SECTHDR	UWX_ERR_NOSYM  /* Can't read section headers */
167#define ELF_ERR_SECTHDR_SIZE	UWX_ERR_NOSYM  /* Invalid e_shentsize */
168
169#define ELF_ERR_READ_PROGHDR	UWX_ERR_NOSYM  /* Can't read program headers */
170#define ELF_ERR_PROGHDR_SIZE	UWX_ERR_NOSYM  /* Invalid e_phentsize */
171
172#define ELF_ERR_READ_SECTION	UWX_ERR_NOSYM  /* Can't read section contents */
173
174#define ELF_ERR_READ_SYMTAB	UWX_ERR_NOSYM  /* Can't read symbol table */
175#define ELF_ERR_SYMTAB_SIZE	UWX_ERR_NOSYM  /* Invalid sh_entsize for symtab */
176
177
178struct elf_file {
179    uint64_t phoff;
180    uint64_t shoff;
181    uint64_t text_base;
182    uint64_t text_end;
183    alloc_cb allocate_cb;
184    free_cb free_cb;
185    const char *filename;
186    FILE *fd;
187    struct elf_section *sections;
188    struct elf_symbol *symbols;
189    char *symbol_strings;
190    int native_data;
191    int source_class;
192    int source_data;
193    int ehsize;
194    int phentsize;
195    int phnum;
196    int shentsize;
197    int shnum;
198    int nsyms;
199};
200
201struct elf_section {
202    uint64_t flags;
203    uint64_t addr;
204    uint64_t offset;
205    uint64_t size;
206    uint64_t entsize;
207    char *contents;
208    struct elf_symbol *symbols;
209    int type;
210    int link;
211    int info;
212    int nelems;
213};
214
215struct elf_symbol {
216    uint64_t value;
217    char *namep;
218    int name;
219    int type;
220    int shndx;
221};
222
223
224static void elf_swap_bytes(char *buf, char *template)
225{
226    int i;
227    int sz;
228    char temp[16];
229
230    while (sz = *template++) {
231	if (sz > 16)
232	    exit(1);
233	for (i = 0; i < sz; i++)
234	    temp[i] = buf[i];
235	for (i = 0; i < sz; i++)
236	    buf[i] = temp[sz-i-1];
237	buf += sz;
238    }
239}
240
241
242static int elf_read_section(struct elf_file *ef, int shndx)
243{
244    struct elf_section *sect;
245
246    if (shndx < 0 || shndx > ef->shnum)
247	return 0;
248
249    sect = &ef->sections[shndx];
250
251    /* Return if section has already been read */
252    if (sect->contents != NULL)
253	return 0;
254
255    sect->contents = (*ef->allocate_cb)(sect->size);
256    if (sect->contents == NULL)
257	return ELF_ERR_NOMEM;
258
259    fseek(ef->fd, (long)sect->offset, SEEK_SET);
260    if (fread(sect->contents, 1, sect->size, ef->fd) != sect->size)
261	return ELF_ERR_READ_SECTION;
262
263    return 0;
264}
265
266
267static char template_elf32_sym[] = {4, 4, 4, 1, 1, 2, 0};
268static char template_elf64_sym[] = {4, 1, 1, 2, 8, 8, 0};
269
270static int elf_read_symtab_section(struct elf_file *ef, int shndx)
271{
272    int i;
273    int nsyms;
274    long size;
275    union {
276	Elf32_Sym sym32;
277	Elf64_Sym sym64;
278    } sym;
279    struct elf_section *sect;
280    struct elf_symbol *syms;
281    struct elf_symbol *symp;
282    char *strtab;
283
284    sect = &ef->sections[shndx];
285
286    /* Return if section has already been read */
287    if (sect->symbols != NULL)
288	return 0;
289
290    if (ef->source_class == ELFCLASS32) {
291	if (sect->entsize != sizeof(sym.sym32))
292	    return ELF_ERR_SYMTAB_SIZE;
293    }
294    else {
295	if (sect->entsize != sizeof(sym.sym64))
296	    return ELF_ERR_SYMTAB_SIZE;
297    }
298
299    nsyms = sect->nelems;
300    syms = (struct elf_symbol *)
301			(*ef->allocate_cb)(sizeof(struct elf_symbol) * nsyms);
302    if (syms == NULL)
303	return ELF_ERR_NOMEM;
304
305    /* Read the symbol table */
306    fseek(ef->fd, (long)sect->offset, SEEK_SET);
307    for (i = 0; i < nsyms; i++) {
308
309	symp = &syms[i];
310
311	/* Read the next symbol table entry */
312	if (fread((char *)&sym, sect->entsize, 1, ef->fd) != 1) {
313	    (*ef->free_cb)(syms);
314	    return ELF_ERR_READ_SYMTAB;
315	}
316
317	/* Get fields from appropriate structure */
318	if (ef->source_class == ELFCLASS32) {
319	    /* Swap bytes if necessary */
320	    if (ef->source_data != ef->native_data)
321		elf_swap_bytes((char *)&sym, template_elf32_sym);
322	    symp->name = sym.sym32.st_name;
323	    symp->type = sym.sym32.st_info & 0x0f;
324	    symp->shndx = sym.sym32.st_shndx;
325	    symp->value = sym.sym32.st_value;
326	}
327	else {
328	    /* Swap bytes if necessary */
329	    if (ef->source_data != ef->native_data)
330		elf_swap_bytes((char *)&sym, template_elf64_sym);
331	    symp->name = sym.sym64.st_name;
332	    symp->type = sym.sym64.st_info & 0x0f;
333	    symp->shndx = sym.sym64.st_shndx;
334	    symp->value = sym.sym64.st_value;
335	}
336	symp->namep = NULL;
337
338    }
339
340    /* Read the symbol string table and convert section names */
341    /* from string table offsets to pointers */
342    if (sect->link > 0 && sect->link < ef->shnum) {
343	if (elf_read_section(ef, sect->link) == 0) {
344	    strtab = ef->sections[sect->link].contents;
345	    for (i = 0; i < nsyms; i++) {
346		symp = &syms[i];
347		symp->namep = strtab + symp->name;
348	    }
349	    ef->symbol_strings = strtab;
350	    ef->sections[sect->link].contents = NULL;
351	}
352    }
353
354    sect->symbols = syms;
355    return 0;
356}
357
358
359static char template_elf32_phdr[] = {4, 4, 4, 4, 4, 4, 4, 4, 0};
360static char template_elf64_phdr[] = {4, 4, 8, 8, 8, 8, 8, 8, 0};
361
362static int elf_read_prog_hdrs(struct elf_file *ef)
363{
364    int i;
365    union {
366	Elf32_Phdr hdr32;
367	Elf64_Phdr hdr64;
368    } header;
369    uint64_t vaddr;
370    uint64_t memsz;
371    uint64_t unwind_base;
372    int type;
373
374    if (ef->phnum == 0)
375	return 0;
376
377    if (ef->source_class == ELFCLASS32) {
378	if (ef->phentsize != sizeof(header.hdr32))
379	    return ELF_ERR_PROGHDR_SIZE;
380    }
381    else {
382	if (ef->phentsize != sizeof(header.hdr64))
383	    return ELF_ERR_PROGHDR_SIZE;
384    }
385
386    /* Look for the PT_IA_64_UNWIND segment */
387    /* (That will help us identify the text segment) */
388
389    fseek(ef->fd, (long)ef->phoff, SEEK_SET);
390    for (i = 0; i < ef->phnum; i++) {
391
392	/* Read the next program header */
393	if (fread((char *)&header, ef->phentsize, 1, ef->fd) != 1)
394	    return ELF_ERR_READ_PROGHDR;
395
396	/* Get fields from appropriate structure */
397	if (ef->source_class == ELFCLASS32) {
398	    /* Swap bytes in header fields if necessary */
399	    if (ef->source_data != ef->native_data)
400		elf_swap_bytes((char *)&header, template_elf32_phdr);
401	    type = header.hdr32.p_type;
402	    vaddr = header.hdr32.p_vaddr;
403	}
404	else {
405	    /* Swap bytes in header fields if necessary */
406	    if (ef->source_data != ef->native_data)
407		elf_swap_bytes((char *)&header, template_elf64_phdr);
408	    type = header.hdr64.p_type;
409	    vaddr = header.hdr64.p_vaddr;
410	}
411
412	if (type == PT_IA_64_UNWIND) {
413	    unwind_base = vaddr;
414	    break;
415	}
416
417    }
418
419    /* Now look for the PT_LOAD segment that includes the unwind segment */
420
421    fseek(ef->fd, (long)ef->phoff, SEEK_SET);
422    for (i = 0; i < ef->phnum; i++) {
423
424	/* Read the next program header */
425	if (fread((char *)&header, ef->phentsize, 1, ef->fd) != 1)
426	    return ELF_ERR_READ_PROGHDR;
427
428	/* Get fields from appropriate structure */
429	if (ef->source_class == ELFCLASS32) {
430	    /* Swap bytes in header fields if necessary */
431	    if (ef->source_data != ef->native_data)
432		elf_swap_bytes((char *)&header, template_elf32_phdr);
433	    type = header.hdr32.p_type;
434	    vaddr = header.hdr32.p_vaddr;
435	    memsz = header.hdr32.p_memsz;
436	}
437	else {
438	    /* Swap bytes in header fields if necessary */
439	    if (ef->source_data != ef->native_data)
440		elf_swap_bytes((char *)&header, template_elf64_phdr);
441	    type = header.hdr64.p_type;
442	    vaddr = header.hdr64.p_vaddr;
443	    memsz = header.hdr64.p_memsz;
444	}
445
446	if (type == PT_LOAD &&
447		vaddr <= unwind_base && unwind_base < vaddr + memsz) {
448	    ef->text_base = vaddr;
449	    ef->text_end = vaddr + memsz;
450	    break;
451	}
452
453    }
454
455    return 0;
456}
457
458
459static char template_elf32_shdr[] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0};
460static char template_elf64_shdr[] = {4, 4, 8, 8, 8, 8, 4, 4, 8, 8, 0};
461
462static int elf_read_sect_hdrs(struct elf_file *ef)
463{
464    int i;
465    long size;
466    int err;
467    union {
468	Elf32_Shdr hdr32;
469	Elf64_Shdr hdr64;
470    } header;
471    struct elf_section *sect;
472    char *shstrtab;
473
474    if (ef->source_class == ELFCLASS32) {
475	if (ef->shentsize != sizeof(header.hdr32))
476	    return ELF_ERR_SECTHDR_SIZE;
477    }
478    else {
479	if (ef->shentsize != sizeof(header.hdr64))
480	    return ELF_ERR_SECTHDR_SIZE;
481    }
482
483    fseek(ef->fd, (long)ef->shoff, SEEK_SET);
484    ef->sections = (struct elf_section *)
485		    (*ef->allocate_cb)(sizeof(struct elf_section) * ef->shnum);
486    if (ef->sections == NULL)
487	return ELF_ERR_NOMEM;
488
489    /* Read the section header table */
490    for (i = 0; i < ef->shnum; i++) {
491
492	sect = &ef->sections[i];
493
494	/* Read the next section header */
495	if (fread((char *)&header, ef->shentsize, 1, ef->fd) != 1) {
496	    (*ef->free_cb)(ef->sections);
497	    return ELF_ERR_READ_SECTHDR;
498	}
499
500	/* Get fields from appropriate structure */
501	if (ef->source_class == ELFCLASS32) {
502	    /* Swap bytes in header fields if necessary */
503	    if (ef->source_data != ef->native_data)
504		elf_swap_bytes((char *)&header, template_elf32_shdr);
505	    sect->type = header.hdr32.sh_type;
506	    sect->flags = header.hdr32.sh_flags;
507	    sect->addr = header.hdr32.sh_addr;
508	    sect->offset = header.hdr32.sh_offset;
509	    sect->size = header.hdr32.sh_size;
510	    sect->link = header.hdr32.sh_link;
511	    sect->info = header.hdr32.sh_info;
512	    sect->entsize = header.hdr32.sh_entsize;
513	}
514	else {
515	    /* Swap bytes in header fields if necessary */
516	    if (ef->source_data != ef->native_data)
517		elf_swap_bytes((char *)&header, template_elf64_shdr);
518	    sect->type = header.hdr64.sh_type;
519	    sect->flags = header.hdr64.sh_flags;
520	    sect->addr = header.hdr64.sh_addr;
521	    sect->offset = header.hdr64.sh_offset;
522	    sect->size = header.hdr64.sh_size;
523	    sect->link = header.hdr64.sh_link;
524	    sect->info = header.hdr64.sh_info;
525	    sect->entsize = header.hdr64.sh_entsize;
526	}
527	sect->contents = NULL;
528	sect->symbols = NULL;
529	if (sect->entsize > 0)
530	    sect->nelems = sect->size / sect->entsize;
531
532    }
533
534    return 0;
535}
536
537
538static char template_elf32_ehdr[] = {2, 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 0};
539static char template_elf64_ehdr[] = {2, 2, 4, 8, 8, 8, 4, 2, 2, 2, 2, 2, 2, 0};
540
541static int elf_read_header(struct elf_file *ef)
542{
543    union {
544	char ident[EI_NIDENT];
545	Elf32_Ehdr hdr32;
546	Elf64_Ehdr hdr64;
547    } header;
548
549    /* Read the ELF header */
550    fseek(ef->fd, 0L, SEEK_SET);
551    if (fread((char *)header.ident, EI_NIDENT, 1, ef->fd) != 1) {
552	return ELF_ERR_NOHEADER;
553    }
554
555    /* Verify that this is an ELF file */
556    if (header.ident[EI_MAG0] != ELFMAG0 ||
557	header.ident[EI_MAG1] != ELFMAG1 ||
558	header.ident[EI_MAG2] != ELFMAG2 ||
559	header.ident[EI_MAG3] != ELFMAG3) {
560	return ELF_ERR_NOTELF;
561    }
562
563    /* Get header fields from the byte array e_ident */
564    /* (These are independent of EI_CLASS and EI_DATA) */
565    ef->source_class = header.ident[EI_CLASS];
566    ef->source_data = header.ident[EI_DATA];
567
568    /* Verify EI_CLASS and EI_DATA */
569    if (header.ident[EI_CLASS] != ELFCLASS32 &&
570	header.ident[EI_CLASS] != ELFCLASS64) {
571	return ELF_ERR_INVALID_CLASS;
572    }
573    if (header.ident[EI_DATA] != ELFDATA2LSB &&
574	header.ident[EI_DATA] != ELFDATA2MSB) {
575	return ELF_ERR_INVALID_DATA;
576    }
577
578    /* Get remaining header fields from appropriate structure */
579    if (ef->source_class == ELFCLASS32) {
580	if (fread((char *)&header.hdr32 + EI_NIDENT,
581			sizeof(header.hdr32) - EI_NIDENT, 1, ef->fd) != 1)
582	    return ELF_ERR_NOHEADER;
583	/* Swap bytes in header fields if necessary */
584	if (ef->source_data != ef->native_data)
585	    elf_swap_bytes((char *)&header + EI_NIDENT, template_elf32_ehdr);
586	ef->phoff = header.hdr32.e_phoff;
587	ef->shoff = header.hdr32.e_shoff;
588	ef->ehsize = header.hdr32.e_ehsize;
589	ef->phentsize = header.hdr32.e_phentsize;
590	ef->phnum = header.hdr32.e_phnum;
591	ef->shentsize = header.hdr32.e_shentsize;
592	ef->shnum = header.hdr32.e_shnum;
593	if (ef->ehsize != sizeof(header.hdr32)) {
594	    return ELF_ERR_HEADER_SIZE;
595	}
596    }
597    else {
598	if (fread((char *)&header.hdr64 + EI_NIDENT,
599			sizeof(header.hdr64) - EI_NIDENT, 1, ef->fd) != 1)
600	    return ELF_ERR_NOHEADER;
601	/* Swap bytes in header fields if necessary */
602	if (ef->source_data != ef->native_data)
603	    elf_swap_bytes((char *)&header + EI_NIDENT, template_elf64_ehdr);
604	ef->phoff = header.hdr64.e_phoff;
605	ef->shoff = header.hdr64.e_shoff;
606	ef->ehsize = header.hdr64.e_ehsize;
607	ef->phentsize = header.hdr64.e_phentsize;
608	ef->phnum = header.hdr64.e_phnum;
609	ef->shentsize = header.hdr64.e_shentsize;
610	ef->shnum = header.hdr64.e_shnum;
611	if (ef->ehsize != sizeof(header.hdr64)) {
612	    return ELF_ERR_HEADER_SIZE;
613	}
614    }
615
616    return 0;
617}
618
619
620static struct elf_file *elf_new(struct uwx_env *env)
621{
622    int native_be;
623    char *p;
624    struct elf_file *ef;
625
626    ef = (struct elf_file *)(*env->allocate_cb)(sizeof(struct elf_file));
627    if (ef == NULL)
628	return NULL;
629
630    /* Determine the native byte order */
631    p = (char *)&native_be;
632    native_be = 1;	/* Assume big-endian */
633    *p = 0;		/* Sets be == 0 only if little-endian */
634
635    ef->allocate_cb = env->allocate_cb;
636    ef->free_cb = env->free_cb;
637    ef->filename = NULL;
638    ef->native_data = (native_be ? ELFDATA2MSB : ELFDATA2LSB);
639    ef->fd = NULL;
640    ef->source_class = 0;
641    ef->source_data = 0;
642    ef->phoff = 0;
643    ef->shoff = 0;
644    ef->text_base = 0;
645    ef->text_end = 0;
646    ef->ehsize = 0;
647    ef->phentsize = 0;
648    ef->phnum = 0;
649    ef->shentsize = 0;
650    ef->shnum = 0;
651    ef->sections = NULL;
652    ef->symbols = NULL;
653    ef->symbol_strings = NULL;
654    ef->nsyms = 0;
655    return ef;
656}
657
658
659static int elf_open(struct elf_file *ef, const char *filename)
660{
661    int err;
662
663    ef->filename = filename;
664
665    ef->fd = fopen(filename, "r");
666    if (ef->fd == NULL)
667	return ELF_ERR_OPEN;
668
669    if ((err = elf_read_header(ef)) != 0)
670	return err;
671
672    if ((err = elf_read_sect_hdrs(ef)) != 0)
673	return err;
674
675    if ((err = elf_read_prog_hdrs(ef)) != 0)
676	return err;
677
678    return 0;
679}
680
681
682static void elf_free_sections(struct elf_file *ef)
683{
684    int i;
685    struct elf_section *sect;
686
687    for (i = 0; i < ef->shnum; i++) {
688	sect = &ef->sections[i];
689	if (sect->contents != NULL)
690	    (*ef->free_cb)(sect->contents);
691	if ((sect->type == SHT_SYMTAB || sect->type == SHT_DYNSYM)
692						&& sect->symbols != NULL)
693	    (*ef->free_cb)(sect->symbols);
694    }
695    (*ef->free_cb)(ef->sections);
696}
697
698
699static void elf_close(struct elf_file *ef)
700{
701    if (ef->fd != NULL) {
702	fclose(ef->fd);
703	ef->fd = NULL;
704    }
705}
706
707
708static void elf_free(struct elf_file *ef)
709{
710    elf_close(ef);
711    if (ef->sections != NULL)
712	elf_free_sections(ef);
713    (*ef->free_cb)(ef);
714}
715
716
717static int elf_read_symbols(struct elf_file *ef)
718{
719    int i;
720    int err;
721    struct elf_section *sect;
722
723    for (i = 1; i < ef->shnum; i++) {
724	sect = &ef->sections[i];
725	if (sect->type == SHT_SYMTAB) {
726	    if (elf_read_symtab_section(ef, i) == 0) {
727		ef->symbols = sect->symbols;
728		ef->nsyms = sect->nelems;
729#ifdef DEBUG_SYMBOLS
730		printf("Read %d symbols from SHT_SYMTAB section\n", ef->nsyms);
731#endif /* DEBUG_SYMBOLS */
732		return 0;
733	    }
734	}
735    }
736    for (i = 1; i < ef->shnum; i++) {
737	sect = &ef->sections[i];
738	if (sect->type == SHT_DYNSYM) {
739	    if (elf_read_symtab_section(ef, i) == 0) {
740		ef->symbols = sect->symbols;
741		ef->nsyms = sect->nelems;
742#ifdef DEBUG_SYMBOLS
743		printf("Read %d symbols from SHT_DYNSYM section\n", ef->nsyms);
744#endif /* DEBUG_SYMBOLS */
745		return 0;
746	    }
747	}
748    }
749    return UWX_ERR_NOSYM;
750}
751
752
753#define SYM_IS_DEFINED(sym) \
754		((sym)->shndx != SHN_UNDEF)
755
756#define SYM_IS_IN_TEXT_SEGMENT(value) \
757		((value) >= ef->text_base && (value) < ef->text_end)
758
759#define SYM_HAS_INTERESTING_TYPE(type) ( \
760		(type) == STT_FUNC || \
761		(type) == STT_OBJECT || \
762		(type) == STT_HP_STUB \
763		)
764
765#define SYM_IS_INTERESTING(sym) ( \
766		SYM_IS_DEFINED(sym) && \
767		SYM_IS_IN_TEXT_SEGMENT((sym)->value) && \
768		SYM_HAS_INTERESTING_TYPE((sym)->type) \
769		)
770
771int uwx_read_func_symbols(
772    struct uwx_env *env,
773    struct uwx_symbol_cache *cache,
774    char *module_name)
775{
776    int i, j;
777    int status;
778    struct elf_file *ef;
779    struct elf_symbol *sym;
780    int nfuncsyms;
781    char **names;
782    uint64_t *values;
783
784    if (module_name != NULL &&
785	    cache->module_name != NULL &&
786		strcmp(module_name, cache->module_name) == 0)
787	return UWX_OK;
788
789    if (cache->sym_names != NULL)
790	(*env->free_cb)(cache->sym_names);
791    if (cache->sym_values != NULL)
792	(*env->free_cb)(cache->sym_values);
793    if (cache->strings != NULL)
794	(*env->free_cb)(cache->strings);
795
796    ef = elf_new(env);
797    if (ef == NULL)
798	return UWX_ERR_NOMEM;
799    status = elf_open(ef, module_name);
800    if (status != 0)
801	return UWX_ERR_NOSYM;
802    status = elf_read_symbols(ef);
803    if (status != 0)
804	return UWX_ERR_NOSYM;
805
806    nfuncsyms = 0;
807    for (i = 0; i < ef->nsyms; i++) {
808	sym = &ef->symbols[i];
809	if (SYM_IS_INTERESTING(sym))
810	    nfuncsyms++;
811    }
812
813    names = (char **)(*env->allocate_cb)(nfuncsyms * sizeof(char *));
814    if (names == NULL)
815	return UWX_ERR_NOMEM;
816    values = (uint64_t *)(*env->allocate_cb)(nfuncsyms * sizeof(uint64_t));
817    if (values == NULL)
818	return UWX_ERR_NOMEM;
819
820    j = 0;
821    for (i = 0; i < ef->nsyms; i++) {
822	sym = &ef->symbols[i];
823	if (SYM_IS_INTERESTING(sym)) {
824	    if (j >= nfuncsyms) /* should not happen! */
825		break;
826	    names[j] = sym->namep;
827	    values[j] = sym->value - ef->text_base;
828	    j++;
829	}
830    }
831
832    cache->module_name = (char *)(*env->allocate_cb)(strlen(module_name)+1);
833    if (cache->module_name != NULL) {
834	strcpy(cache->module_name, module_name);
835	cache->nsyms = nfuncsyms;
836	cache->sym_names = names;
837	cache->sym_values = values;
838	cache->strings = ef->symbol_strings;
839	ef->symbol_strings = NULL;
840    }
841
842    elf_close(ef);
843    elf_free(ef);
844
845#ifdef DEBUG_SYMBOLS
846    printf("Cached %d interesting symbols\n", nfuncsyms);
847#endif /* DEBUG_SYMBOLS */
848
849    return UWX_OK;
850}
851