diff options
Diffstat (limited to 'agent/tcf/services/tcf_elf.c')
-rw-r--r-- | agent/tcf/services/tcf_elf.c | 1454 |
1 files changed, 1454 insertions, 0 deletions
diff --git a/agent/tcf/services/tcf_elf.c b/agent/tcf/services/tcf_elf.c new file mode 100644 index 00000000..97f40bce --- /dev/null +++ b/agent/tcf/services/tcf_elf.c @@ -0,0 +1,1454 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ + +/* + * This module implements reading and caching of ELF files. + */ + +#if defined(__GNUC__) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +#endif + +#include <config.h> + +#if ENABLE_ELF + +#include <stddef.h> +#include <assert.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <framework/myalloc.h> +#include <framework/exceptions.h> +#include <framework/events.h> +#include <framework/trace.h> +#include <services/tcf_elf.h> +#include <services/memorymap.h> +#include <services/dwarfcache.h> +#include <services/pathmap.h> + +#if defined(_WRS_KERNEL) +#elif defined(WIN32) +#else +# include <sys/mman.h> +# define USE_MMAP +#endif + +#define MIN_FILE_AGE 3 +#define MAX_FILE_AGE 60 + +typedef struct FileINode { + struct FileINode * next; + char * name; + ino_t ino; +} FileINode; + +static ELF_File * files = NULL; +static FileINode * inodes = NULL; +static ELFCloseListener * listeners = NULL; +static U4_T listeners_cnt = 0; +static U4_T listeners_max = 0; +static int elf_cleanup_posted = 0; +static ino_t elf_ino_cnt = 0; + +#if ENABLE_DebugContext + +static Context * elf_list_ctx; +static unsigned elf_list_pos; +static MemoryMap elf_list; + +static MemoryMap elf_map; + +#endif + +void elf_add_close_listener(ELFCloseListener listener) { + if (listeners_cnt >= listeners_max) { + listeners_max = listeners_max == 0 ? 16 : listeners_max * 2; + listeners = (ELFCloseListener *)loc_realloc(listeners, sizeof(ELFCloseListener) * listeners_max); + } + listeners[listeners_cnt++] = listener; +} + +static void elf_dispose(ELF_File * file) { + U4_T n; + trace(LOG_ELF, "Dispose ELF file cache %s", file->name); + for (n = 0; n < listeners_cnt; n++) { + listeners[n](file); + } + if (file->fd >= 0) close(file->fd); + if (file->sections != NULL) { + for (n = 0; n < file->section_cnt; n++) { + ELF_Section * s = file->sections + n; +#ifdef USE_MMAP + if (s->mmap_addr != NULL) { + s->data = NULL; + munmap(s->mmap_addr, s->mmap_size); + } +#endif + loc_free(s->data); + loc_free(s->sym_addr_table); + loc_free(s->sym_names_hash); + loc_free(s->sym_names_next); + } + loc_free(file->sections); + } + release_error_report(file->error); + loc_free(file->pheaders); + loc_free(file->str_pool); + loc_free(file->debug_info_file_name); + loc_free(file->name); + loc_free(file); +} + +static void elf_cleanup_event(void * arg) { + ELF_File * prev = NULL; + ELF_File * file = files; + + assert(elf_cleanup_posted); + elf_cleanup_posted = 0; + while (file != NULL) { + file->age++; + if (file->age > MAX_FILE_AGE || (file->age > MIN_FILE_AGE && list_is_empty(&context_root))) { + ELF_File * next = file->next; + elf_dispose(file); + file = next; + if (prev != NULL) prev->next = file; + else files = file; + } + else { + prev = file; + file = file->next; + } + } + + file = files; + while (file != NULL) { + struct stat st; + if (!file->mtime_changed && stat(file->name, &st) == 0) { + if (st.st_ino == 0) st.st_ino = file->ino; + if (file->dev == st.st_dev && file->ino == st.st_ino && file->mtime != st.st_mtime) { + file->mtime_changed = 1; + } + } + file = file->next; + } + + if (files != NULL) { + post_event_with_delay(elf_cleanup_event, NULL, 1000000); + elf_cleanup_posted = 1; + } + else if (list_is_empty(&context_root)) { + while (inodes != NULL) { + FileINode * n = inodes; + inodes = n->next; + loc_free(n->name); + loc_free(n); + } + } +} + +static ino_t add_ino(const char * fnm, ino_t ino) { + FileINode * n = (FileINode *)loc_alloc_zero(sizeof(*n)); + n->next = inodes; + n->name = loc_strdup(fnm); + n->ino = ino; + inodes = n; + return ino; +} + +static ino_t elf_ino(const char * fnm) { + /* + * Number of the information node (the inode) for the file is used as file ID. + * Since some file systems don't support inodes, this function is used in such cases + * to generate virtual inode numbers to be used as file IDs. + */ + char * abs = NULL; + FileINode * n = inodes; + while (n != NULL) { + if (strcmp(n->name, fnm) == 0) return n->ino; + n = n->next; + } + abs = canonicalize_file_name(fnm); + if (abs == NULL) return add_ino(fnm, 0); + n = inodes; + while (n != NULL) { + if (strcmp(n->name, abs) == 0) { + free(abs); + return add_ino(fnm, n->ino); + } + n = n->next; + } + if (elf_ino_cnt == 0) elf_ino_cnt++; + add_ino(fnm, elf_ino_cnt); + if (strcmp(abs, fnm) != 0) add_ino(abs, elf_ino_cnt); + free(abs); + return elf_ino_cnt++; +} + +static ELF_File * find_open_file_by_name(const char * name) { + ELF_File * prev = NULL; + ELF_File * file = files; + while (file != NULL) { + if (strcmp(name, file->name) == 0) { + if (prev != NULL) { + prev->next = file->next; + file->next = files; + files = file; + } + file->age = 0; + return file; + } + prev = file; + file = file->next; + } + return NULL; +} + +void swap_bytes(void * buf, size_t size) { + size_t i, j, n; + char * p = (char *)buf; + n = size >> 1; + for (i = 0, j = size - 1; i < n; i++, j--) { + char x = p[i]; + p[i] = p[j]; + p[j] = x; + } +} + +static char * get_debug_info_file_name(ELF_File * file, int * error) { + unsigned idx; + + for (idx = 1; idx < file->section_cnt; idx++) { + ELF_Section * sec = file->sections + idx; + if (sec->size == 0) continue; + if (sec->type == SHT_NOTE && (sec->flags & SHF_ALLOC)) { + unsigned offs = 0; + if (elf_load(sec) < 0) { + *error = errno; + return NULL; + } + while (offs < sec->size) { + U4_T name_sz = *(U4_T *)((U1_T *)sec->data + offs); + U4_T desc_sz = *(U4_T *)((U1_T *)sec->data + offs + 4); + U4_T type = *(U4_T *)((U1_T *)sec->data + offs + 8); + char * name = NULL; + offs += 12; + if (file->byte_swap) { + SWAP(name_sz); + SWAP(desc_sz); + SWAP(type); + } + name = (char *)((U1_T *)sec->data + offs); + offs += name_sz; + while (offs % 4 != 0) offs++; + if (type == 3 && strcmp(name, "GNU") == 0) { + char fnm[FILE_PATH_SIZE]; + char * lnm = fnm; + struct stat buf; + char id[64]; + size_t id_size = 0; + U1_T * desc = (U1_T *)sec->data + offs; + U4_T i = 0; + while (i < desc_sz) { + U1_T j = (desc[i] >> 4) & 0xf; + U1_T k = desc[i++] & 0xf; + id[id_size++] = j < 10 ? '0' + j : 'a' + j - 10; + id[id_size++] = k < 10 ? '0' + k : 'a' + k - 10; + } + id[id_size++] = 0; + trace(LOG_ELF, "Found GNU build ID %s", id); + snprintf(fnm, sizeof(fnm), "/usr/lib/debug/.build-id/%.2s/%s.debug", id, id + 2); +#if SERVICE_PathMap + lnm = apply_path_map(NULL, NULL, lnm, PATH_MAP_TO_LOCAL); +#endif + if (stat(lnm, &buf) == 0) return loc_strdup(lnm); + return NULL; + } + offs += desc_sz; + while (offs % 4 != 0) offs++; + } + } + else if (sec->name != NULL && strcmp(sec->name, ".gnu_debuglink") == 0) { + if (elf_load(sec) < 0) { + *error = errno; + return NULL; + } + else { + /* TODO: check debug info CRC */ + char fnm[FILE_PATH_SIZE]; + char * lnm = fnm; + struct stat buf; + char * name = (char *)sec->data; + int l = strlen(file->name); + while (l > 0 && file->name[l - 1] != '/' && file->name[l - 1] != '\\') l--; + if (strcmp(file->name + l, name) != 0) { + snprintf(fnm, sizeof(fnm), "%.*s%s", l, file->name, name); + if (stat(fnm, &buf) == 0) return loc_strdup(fnm); + } + snprintf(fnm, sizeof(fnm), "%.*s.debug/%s", l, file->name, name); + if (stat(fnm, &buf) == 0) return loc_strdup(fnm); + snprintf(fnm, sizeof(fnm), "/usr/lib/debug%.*s%s", l, file->name, name); +#if SERVICE_PathMap + lnm = apply_path_map(NULL, NULL, lnm, PATH_MAP_TO_LOCAL); +#endif + if (stat(lnm, &buf) == 0) return loc_strdup(lnm); + return NULL; + } + } + } + return NULL; +} + +static ELF_File * create_elf_cache(const char * file_name) { + struct stat st; + int error = 0; + ELF_File * file = NULL; + unsigned str_index = 0; + + trace(LOG_ELF, "Create ELF file cache %s", file_name); + + file = (ELF_File *)loc_alloc_zero(sizeof(ELF_File)); + file->name = loc_strdup(file_name); + file->fd = -1; + + if (stat(file_name, &st) < 0) { + error = errno; + memset(&st, 0, sizeof(st)); + } + else if (st.st_ino == 0) { + st.st_ino = elf_ino(file_name); + } + + file->dev = st.st_dev; + file->ino = st.st_ino; + file->mtime = st.st_mtime; + + if (error == 0 && (file->fd = open(file->name, O_RDONLY | O_BINARY, 0)) < 0) error = errno; + + if (error == 0) { + Elf32_Ehdr hdr; + memset(&hdr, 0, sizeof(hdr)); + if (read(file->fd, (char *)&hdr, sizeof(hdr)) < 0) error = errno; + if (error == 0 && strncmp((char *)hdr.e_ident, ELFMAG, SELFMAG) != 0) { + error = set_errno(ERR_INV_FORMAT, "Unsupported ELF identification code"); + } + if (error == 0) { + if (hdr.e_ident[EI_DATA] == ELFDATA2LSB) { + file->big_endian = 0; + } + else if (hdr.e_ident[EI_DATA] == ELFDATA2MSB) { + file->big_endian = 1; + } + else { + error = set_errno(ERR_INV_FORMAT, "Invalid ELF data encoding ID"); + } + file->byte_swap = file->big_endian != big_endian_host(); + } + if (error != 0) { + /* Nothing */ + } + else if (hdr.e_ident[EI_CLASS] == ELFCLASS32) { + if (file->byte_swap) { + SWAP(hdr.e_type); + SWAP(hdr.e_machine); + SWAP(hdr.e_version); + SWAP(hdr.e_entry); + SWAP(hdr.e_phoff); + SWAP(hdr.e_shoff); + SWAP(hdr.e_flags); + SWAP(hdr.e_ehsize); + SWAP(hdr.e_phentsize); + SWAP(hdr.e_phnum); + SWAP(hdr.e_shentsize); + SWAP(hdr.e_shnum); + SWAP(hdr.e_shstrndx); + } + file->type = hdr.e_type; + file->machine = hdr.e_machine; + file->os_abi = hdr.e_ident[EI_OSABI]; + if (error == 0 && hdr.e_type != ET_EXEC && hdr.e_type != ET_DYN && hdr.e_type != ET_REL) { + error = set_errno(ERR_INV_FORMAT, "Invalid ELF type ID"); + } + if (error == 0 && hdr.e_version != EV_CURRENT) { + error = set_errno(ERR_INV_FORMAT, "Unsupported ELF version"); + } + if (error == 0 && hdr.e_shoff == 0) { + error = set_errno(ERR_INV_FORMAT, "Invalid section header table's file offset"); + } + if (error == 0 && lseek(file->fd, hdr.e_shoff, SEEK_SET) == (off_t)-1) error = errno; + if (error == 0) { + unsigned cnt = 0; + file->sections = (ELF_Section *)loc_alloc_zero(sizeof(ELF_Section) * hdr.e_shnum); + file->section_cnt = hdr.e_shnum; + while (error == 0 && cnt < hdr.e_shnum) { + int rd = 0; + Elf32_Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + if (error == 0 && sizeof(shdr) < hdr.e_shentsize) error = ERR_INV_FORMAT; + if (error == 0 && (rd = read(file->fd, (char *)&shdr, hdr.e_shentsize)) < 0) error = errno; + if (error == 0 && rd != hdr.e_shentsize) error = ERR_INV_FORMAT; + if (error == 0) { + ELF_Section * sec = file->sections + cnt; + if (file->byte_swap) { + SWAP(shdr.sh_name); + SWAP(shdr.sh_type); + SWAP(shdr.sh_flags); + SWAP(shdr.sh_addr); + SWAP(shdr.sh_offset); + SWAP(shdr.sh_size); + SWAP(shdr.sh_link); + SWAP(shdr.sh_info); + SWAP(shdr.sh_addralign); + SWAP(shdr.sh_entsize); + } + sec->file = file; + sec->index = cnt; + sec->name_offset = shdr.sh_name; + sec->type = shdr.sh_type; + sec->alignment = (U4_T)shdr.sh_addralign; + sec->offset = shdr.sh_offset; + sec->size = shdr.sh_size; + sec->flags = shdr.sh_flags; + sec->addr = shdr.sh_addr; + sec->link = shdr.sh_link; + sec->info = shdr.sh_info; + sec->entsize = shdr.sh_entsize; + if (sec->type == SHT_SYMTAB) sec->sym_count = (unsigned)(sec->size / sizeof(Elf32_Sym)); + cnt++; + } + } + } + if (error == 0 && lseek(file->fd, hdr.e_phoff, SEEK_SET) == (off_t)-1) error = errno; + if (error == 0) { + unsigned cnt = 0; + file->pheaders = (ELF_PHeader *)loc_alloc_zero(sizeof(ELF_PHeader) * hdr.e_phnum); + file->pheader_cnt = hdr.e_phnum; + while (error == 0 && cnt < hdr.e_phnum) { + int rd = 0; + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + if (error == 0 && sizeof(phdr) < hdr.e_phentsize) error = ERR_INV_FORMAT; + if (error == 0 && (rd = read(file->fd, (char *)&phdr, hdr.e_phentsize)) < 0) error = errno; + if (error == 0 && rd != hdr.e_phentsize) error = ERR_INV_FORMAT; + if (error == 0) { + ELF_PHeader * p = file->pheaders + cnt; + if (file->byte_swap) { + SWAP(phdr.p_type); + SWAP(phdr.p_offset); + SWAP(phdr.p_vaddr); + SWAP(phdr.p_paddr); + SWAP(phdr.p_filesz); + SWAP(phdr.p_memsz); + SWAP(phdr.p_flags); + SWAP(phdr.p_align); + } + p->type = phdr.p_type; + p->offset = phdr.p_offset; + p->address = phdr.p_vaddr; + p->file_size = phdr.p_filesz; + p->mem_size = phdr.p_memsz; + p->flags = phdr.p_flags; + p->align = phdr.p_align; + cnt++; + } + } + } + str_index = hdr.e_shstrndx; + } + else if (hdr.e_ident[EI_CLASS] == ELFCLASS64) { + Elf64_Ehdr hdr; + file->elf64 = 1; + memset(&hdr, 0, sizeof(hdr)); + if (error == 0 && lseek(file->fd, 0, SEEK_SET) == (off_t)-1) error = errno; + if (error == 0 && read(file->fd, (char *)&hdr, sizeof(hdr)) < 0) error = errno; + if (file->byte_swap) { + SWAP(hdr.e_type); + SWAP(hdr.e_machine); + SWAP(hdr.e_version); + SWAP(hdr.e_entry); + SWAP(hdr.e_phoff); + SWAP(hdr.e_shoff); + SWAP(hdr.e_flags); + SWAP(hdr.e_ehsize); + SWAP(hdr.e_phentsize); + SWAP(hdr.e_phnum); + SWAP(hdr.e_shentsize); + SWAP(hdr.e_shnum); + SWAP(hdr.e_shstrndx); + } + file->type = hdr.e_type; + file->machine = hdr.e_machine; + file->os_abi = hdr.e_ident[EI_OSABI]; + if (error == 0 && hdr.e_type != ET_EXEC && hdr.e_type != ET_DYN && hdr.e_type != ET_REL) { + error = set_errno(ERR_INV_FORMAT, "Invalid ELF type ID"); + } + if (error == 0 && hdr.e_version != EV_CURRENT) { + error = set_errno(ERR_INV_FORMAT, "Unsupported ELF version"); + } + if (error == 0 && hdr.e_shoff == 0) { + error = set_errno(ERR_INV_FORMAT, "Invalid section header table's file offset"); + } + if (error == 0 && lseek(file->fd, hdr.e_shoff, SEEK_SET) == (off_t)-1) error = errno; + if (error == 0) { + unsigned cnt = 0; + file->sections = (ELF_Section *)loc_alloc_zero(sizeof(ELF_Section) * hdr.e_shnum); + file->section_cnt = hdr.e_shnum; + while (error == 0 && cnt < hdr.e_shnum) { + int rd = 0; + Elf64_Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + if (error == 0 && sizeof(shdr) < hdr.e_shentsize) error = ERR_INV_FORMAT; + if (error == 0 && (rd = read(file->fd, (char *)&shdr, hdr.e_shentsize)) < 0) error = errno; + if (error == 0 && rd != hdr.e_shentsize) error = ERR_INV_FORMAT; + if (error == 0) { + ELF_Section * sec = file->sections + cnt; + if (file->byte_swap) { + SWAP(shdr.sh_name); + SWAP(shdr.sh_type); + SWAP(shdr.sh_flags); + SWAP(shdr.sh_addr); + SWAP(shdr.sh_offset); + SWAP(shdr.sh_size); + SWAP(shdr.sh_link); + SWAP(shdr.sh_info); + SWAP(shdr.sh_addralign); + SWAP(shdr.sh_entsize); + } + sec->file = file; + sec->index = cnt; + sec->name_offset = shdr.sh_name; + sec->type = shdr.sh_type; + sec->alignment = (U4_T)shdr.sh_addralign; + sec->offset = shdr.sh_offset; + sec->size = shdr.sh_size; + sec->flags = (U4_T)shdr.sh_flags; + sec->addr = shdr.sh_addr; + sec->link = shdr.sh_link; + sec->info = shdr.sh_info; + sec->entsize = (U4_T)shdr.sh_entsize; + if (sec->type == SHT_SYMTAB) sec->sym_count = (unsigned)(sec->size / sizeof(Elf64_Sym)); + cnt++; + } + } + } + if (error == 0 && lseek(file->fd, hdr.e_phoff, SEEK_SET) == (off_t)-1) error = errno; + if (error == 0) { + unsigned cnt = 0; + file->pheaders = (ELF_PHeader *)loc_alloc_zero(sizeof(ELF_PHeader) * hdr.e_phnum); + file->pheader_cnt = hdr.e_phnum; + while (error == 0 && cnt < hdr.e_phnum) { + int rd = 0; + Elf64_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + if (error == 0 && sizeof(phdr) < hdr.e_phentsize) error = ERR_INV_FORMAT; + if (error == 0 && (rd = read(file->fd, (char *)&phdr, hdr.e_phentsize)) < 0) error = errno; + if (error == 0 && rd != hdr.e_phentsize) error = ERR_INV_FORMAT; + if (error == 0) { + ELF_PHeader * p = file->pheaders + cnt; + if (file->byte_swap) { + SWAP(phdr.p_type); + SWAP(phdr.p_offset); + SWAP(phdr.p_vaddr); + SWAP(phdr.p_paddr); + SWAP(phdr.p_filesz); + SWAP(phdr.p_memsz); + SWAP(phdr.p_flags); + SWAP(phdr.p_align); + } + p->type = phdr.p_type; + p->offset = phdr.p_offset; + p->address = phdr.p_vaddr; + p->file_size = phdr.p_filesz; + p->mem_size = phdr.p_memsz; + p->flags = phdr.p_flags; + p->align = (U4_T)phdr.p_align; + cnt++; + } + } + } + str_index = hdr.e_shstrndx; + } + else { + error = set_errno(ERR_INV_FORMAT, "Invalid ELF class ID"); + } + if (error == 0 && str_index != 0 && str_index < file->section_cnt) { + int rd = 0; + ELF_Section * str = file->sections + str_index; + file->str_pool = (char *)loc_alloc((size_t)str->size); + if (str->offset == 0 || str->size == 0) error = set_errno(ERR_INV_FORMAT, "Invalid ELF string pool offset or size"); + if (error == 0 && lseek(file->fd, str->offset, SEEK_SET) == (off_t)-1) error = errno; + if (error == 0 && (rd = read(file->fd, file->str_pool, (size_t)str->size)) < 0) error = errno; + if (error == 0 && rd != (int)str->size) error = set_errno(ERR_INV_FORMAT, "Cannot read ELF string pool"); + if (error == 0) { + unsigned i; + for (i = 1; i < file->section_cnt; i++) { + ELF_Section * sec = file->sections + i; + sec->name = file->str_pool + sec->name_offset; + } + } + } + } + if (error == 0) { + file->debug_info_file_name = get_debug_info_file_name(file, &error); + if (file->debug_info_file_name) trace(LOG_ELF, "Debug info file found %s", file->debug_info_file_name); + } + if (error != 0) { + trace(LOG_ELF, "Error opening ELF file: %d %s", error, errno_to_str(error)); + file->error = get_error_report(error); + } + if (!elf_cleanup_posted) { + post_event_with_delay(elf_cleanup_event, NULL, 1000000); + elf_cleanup_posted = 1; + } + file->next = files; + return files = file; +} + +ELF_File * elf_open(const char * file_name) { + ELF_File * file = find_open_file_by_name(file_name); + if (file == NULL) file = create_elf_cache(file_name); + if (file->error == NULL) return file; + set_error_report_errno(file->error); + return NULL; +} + +int elf_load(ELF_Section * s) { + + if (s->data != NULL) return 0; + if (s->size == 0) return 0; + + s->relocate = 0; + if (s->type != SHT_REL && s->type != SHT_RELA) { + unsigned i; + for (i = 1; i < s->file->section_cnt; i++) { + ELF_Section * r = s->file->sections + i; + if (r->entsize == 0 || r->size == 0) continue; + if (r->type != SHT_REL && r->type != SHT_RELA) continue; + if (r->info == s->index) { + s->relocate = 1; + break; + } + } + } + +#ifdef USE_MMAP + { + long page = sysconf(_SC_PAGE_SIZE); + off_t offs = (off_t)s->offset; + offs -= offs % page; + s->mmap_size = (size_t)(s->offset - offs) + s->size; + s->mmap_addr = mmap(0, s->mmap_size, PROT_READ, MAP_PRIVATE, s->file->fd, offs); + if (s->mmap_addr == MAP_FAILED) { + s->mmap_addr = NULL; + trace(LOG_ALWAYS, "Cannot mmap section %s in ELF file %s", s->name, s->file->name); + } + else { + s->data = (char *)s->mmap_addr + (size_t)(s->offset - offs); + trace(LOG_ELF, "Section %s in ELF file %s is mapped to %#lx", s->name, s->file->name, s->data); + } + } +#endif + + if (s->data == NULL) { + ELF_File * file = s->file; + if (lseek(file->fd, s->offset, SEEK_SET) == (off_t)-1) return -1; + s->data = loc_alloc((size_t)s->size); + if (read(file->fd, s->data, (size_t)s->size) < 0) { + int err = errno; + loc_free(s->data); + s->data = NULL; + errno = err; + return -1; + } + trace(LOG_ELF, "Section %s in ELF file %s is loaded", s->name, s->file->name); + } + return 0; +} + +#if ENABLE_DebugContext + +static ELF_File * find_open_file_by_inode(dev_t dev, ino_t ino, int64_t mtime) { + ELF_File * prev = NULL; + ELF_File * file = files; + while (file != NULL) { + if (file->dev == dev && file->ino == ino && + (mtime ? file->mtime == mtime : !file->mtime_changed)) { + if (prev != NULL) { + prev->next = file->next; + file->next = files; + files = file; + } + file->age = 0; + return file; + } + prev = file; + file = file->next; + } + return NULL; +} + +static ELF_File * open_memory_region_file(MemoryRegion * r, int * error) { + ELF_File * file = NULL; + ino_t ino = r->ino; + dev_t dev = r->dev; + + if (r->file_name == NULL) return NULL; + if (dev != 0) { + if (ino == 0) ino = elf_ino(r->file_name); + if (ino != 0) file = find_open_file_by_inode(dev, ino, 0); + } + if (file == NULL) file = find_open_file_by_name(r->file_name); + if (file == NULL) file = create_elf_cache(r->file_name); + if (r->dev != 0 && file->dev != r->dev) return NULL; + if (r->ino != 0 && file->ino != r->ino) return NULL; + if (file->error == NULL) return file; + if (error != NULL && *error == 0) { + int no = set_error_report_errno(file->error); + if (get_error_code(no) != ERR_INV_FORMAT) *error = no; + } + return NULL; +} + +static void add_region(MemoryMap * map, MemoryRegion * r) { + if (map->region_cnt >= map->region_max) { + map->region_max += 8; + map->regions = (MemoryRegion *)loc_realloc(map->regions, sizeof(MemoryRegion ) * map->region_max); + } + map->regions[map->region_cnt++] = *r; +} + +static void search_regions(MemoryMap * map, ContextAddress addr0, ContextAddress addr1, MemoryMap * res) { + unsigned i; + for (i = 0; i < map->region_cnt; i++) { + MemoryRegion * r = map->regions + i; + if (r->file_name == NULL) continue; + if (r->addr == 0 && r->size == 0 && r->file_offs == 0 && r->sect_name == NULL) { + int error = 0; + ELF_File * file = open_memory_region_file(r, &error); + if (file != NULL) { + unsigned j; + for (j = 0; j < file->pheader_cnt; j++) { + ELF_PHeader * p = file->pheaders + j; + if (p->type != PT_LOAD) continue; + if (p->address <= addr1 && p->address + p->mem_size > addr0) { + MemoryRegion x; + memset(&x, 0, sizeof(x)); + x.addr = p->address; + x.size = p->mem_size; + x.dev = file->dev; + x.ino = file->ino; + x.file_name = file->name; + x.file_offs = p->offset; + x.flags = MM_FLAG_R | MM_FLAG_W | MM_FLAG_X; + add_region(res, &x); + } + } + } + } + else if (r->addr <= addr1 && r->size == 0 && r->sect_name != NULL) { + int error = 0; + ELF_File * file = open_memory_region_file(r, &error); + if (file != NULL) { + unsigned j; + for (j = 0; j < file->section_cnt; j++) { + ELF_Section * s = file->sections + j; + if (s == NULL || s->name == NULL) continue; + if (strcmp(s->name, r->sect_name)) continue; + if (r->addr + s->size > addr0) { + MemoryRegion x; + memset(&x, 0, sizeof(x)); + x.addr = r->addr; + x.size = s->size; + x.dev = file->dev; + x.ino = file->ino; + x.file_name = file->name; + x.sect_name = r->sect_name; + x.flags = r->flags; + if (x.flags == 0) x.flags = MM_FLAG_R | MM_FLAG_W | MM_FLAG_X; + add_region(res, &x); + } + } + } + } + else if (r->addr <= addr1 && r->addr + r->size > addr0) { + add_region(res, r); + } + } +} + +static int get_map(Context * ctx, ContextAddress addr0, ContextAddress addr1, MemoryMap * map) { + map->region_cnt = 0; + ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); +#if SERVICE_MemoryMap + { + MemoryMap * client_map = NULL; + MemoryMap * target_map = NULL; + if (memory_map_get(ctx, &client_map, &target_map) < 0) return -1; + search_regions(client_map, addr0, addr1, map); + search_regions(target_map, addr0, addr1, map); + } +#else + { + int error = 0; + MemoryMap target_map; + memset(&target_map, 0, sizeof(target_map)); + if (context_get_memory_map(ctx, &target_map) < 0) error = errno; + if (!error) search_regions(&target_map, addr0, addr1, map); + context_clear_memory_map(&target_map); + loc_free(target_map.regions); + if (error) { + errno = error; + return -1; + } + } +#endif + return 0; +} + +ELF_File * elf_open_inode(Context * ctx, dev_t dev, ino_t ino, int64_t mtime) { + unsigned i; + int error = 0; + ELF_File * file = find_open_file_by_inode(dev, ino, mtime); + if (file != NULL) { + if (file->error == NULL) return file; + set_error_report_errno(file->error); + return NULL; + } + if (get_map(ctx, 0, ~(ContextAddress)0, &elf_map) < 0) return NULL; + for (i = 0; i < elf_map.region_cnt; i++) { + MemoryRegion * r = elf_map.regions + i; + file = open_memory_region_file(r, &error); + if (file == NULL) continue; + if (file->dev == dev && file->ino == ino && file->mtime == mtime) return file; + if (file->debug_info_file_name == NULL) continue; + assert(!file->debug_info_file); + file = elf_open(file->debug_info_file_name); + if (file == NULL) { + error = errno; + continue; + } + if (file->dev == dev && file->ino == ino && file->mtime == mtime) return file; + } + if (error == 0) error = ENOENT; + errno = error; + return NULL; +} + +ELF_File * elf_list_first(Context * ctx, ContextAddress addr_min, ContextAddress addr_max) { + elf_list_ctx = ctx; + elf_list_pos = 0; + if (get_map(ctx, addr_min, addr_max, &elf_list) < 0) return NULL; + if (elf_list.region_cnt > 0) { + ELF_File * f = files; + while (f != NULL) { + f->listed = 0; + f = f->next; + } + return elf_list_next(ctx); + } + errno = 0; + return NULL; +} + +ELF_File * elf_list_next(Context * ctx) { + assert(ctx == elf_list_ctx); + assert(elf_list.region_cnt > 0); + while (elf_list_pos < elf_list.region_cnt) { + int error = 0; + ELF_File * file = open_memory_region_file(elf_list.regions + elf_list_pos++, &error); + if (file != NULL) { + if (file->listed) continue; + file->listed = 1; + return file; + } + if (error) { + errno = error; + return NULL; + } + } + errno = 0; + return NULL; +} + +void elf_list_done(Context * ctx) { + assert(ctx == elf_list_ctx); + elf_list_ctx = NULL; + elf_list_pos = 0; + elf_list.region_cnt = 0; +} + +UnitAddressRange * elf_find_unit(Context * ctx, ContextAddress addr_min, ContextAddress addr_max, ContextAddress * range_rt_addr) { + unsigned i, j; + UnitAddressRange * range = NULL; + int error = 0; + + if (get_map(ctx, addr_min, addr_max, &elf_map) < 0) return NULL; + for (i = 0; range == NULL && i < elf_map.region_cnt; i++) { + ContextAddress link_addr_min, link_addr_max; + MemoryRegion * r = elf_map.regions + i; + ELF_File * file = NULL; + assert(r->addr <= addr_max); + assert(r->addr + r->size > addr_min); + file = open_memory_region_file(r, &error); + if (error) exception(error); + if (r->sect_name == NULL) { + for (j = 0; range == NULL && j < file->pheader_cnt; j++) { + U8_T offs_min = 0; + U8_T offs_max = 0; + ELF_PHeader * p = file->pheaders + j; + if (p->type != PT_LOAD) continue; + if (r->flags) { + if ((p->flags & PF_R) && !(r->flags & MM_FLAG_R)) continue; + if ((p->flags & PF_W) && !(r->flags & MM_FLAG_W)) continue; + if ((p->flags & PF_X) && !(r->flags & MM_FLAG_X)) continue; + } + offs_min = addr_min - r->addr + r->file_offs; + offs_max = addr_max - r->addr + r->file_offs; + if (p->offset >= offs_max || p->offset + p->mem_size <= offs_min) continue; + link_addr_min = offs_min - p->offset + p->address; + link_addr_max = offs_max - p->offset + p->address; + if (link_addr_min < p->address) link_addr_min = p->address; + if (link_addr_max >= p->address + p->mem_size) link_addr_max = p->address + p->mem_size; + range = find_comp_unit_addr_range(get_dwarf_cache(file), link_addr_min, link_addr_max); + if (range == NULL && file->debug_info_file_name != NULL && !file->debug_info_file) { + ELF_File * debug = elf_open(file->debug_info_file_name); + if (debug == NULL) exception(errno); + debug->debug_info_file = 1; + if (j < debug->pheader_cnt) { + p = debug->pheaders + j; + link_addr_min = offs_min - p->offset + p->address; + link_addr_max = offs_max - p->offset + p->address; + if (link_addr_min < p->address) link_addr_min = p->address; + if (link_addr_max >= p->address + p->mem_size) link_addr_max = p->address + p->mem_size; + range = find_comp_unit_addr_range(get_dwarf_cache(debug), link_addr_min, link_addr_max); + } + } + if (range != NULL && range_rt_addr != NULL) { + *range_rt_addr = range->mAddr - p->address + p->offset - r->file_offs + r->addr; + } + } + } + else { + unsigned idx; + for (idx = 1; range == NULL && idx < file->section_cnt; idx++) { + ELF_Section * sec = file->sections + idx; + if (sec->name != NULL && strcmp(sec->name, r->sect_name) == 0) { + link_addr_min = addr_min - r->addr + sec->addr; + link_addr_max = addr_max - r->addr + sec->addr; + if (link_addr_min < sec->addr) link_addr_min = sec->addr; + if (link_addr_max >= sec->addr + sec->size) link_addr_max = sec->addr + sec->size; + range = find_comp_unit_addr_range(get_dwarf_cache(file), link_addr_min, link_addr_max); + if (range != NULL && range_rt_addr != NULL) { + *range_rt_addr = range->mAddr - sec->addr + r->addr; + } + } + } + } + } +/* TODO: lazy reading of comp unit objects */ +#if 0 + if (range != NULL) { + load_comp_unit_children(range->mUnit); + if (range->mUnit->mBaseTypes != NULL) { + load_comp_unit_children(range->mUnit->mBaseTypes); + } + } +#endif + return range; +} + +ContextAddress elf_map_to_run_time_address(Context * ctx, ELF_File * file, ELF_Section * sec, ContextAddress addr) { + unsigned i; + + /* Note: 'addr' is link-time address - it cannot be used as get_map() argument */ + if (get_map(ctx, 0, ~(ContextAddress)0, &elf_map) < 0) return 0; + for (i = 0; i < elf_map.region_cnt; i++) { + MemoryRegion * r = elf_map.regions + i; + int same_file = 0; + if (r->dev == 0) { + same_file = strcmp(file->name, r->file_name) == 0; + } + else { + ino_t ino = r->ino; + if (ino == 0) ino = elf_ino(r->file_name); + same_file = file->ino == ino && file->dev == r->dev; + } + if (!same_file) { + /* Check if the memory map entry has a separate debug info file */ + int error = 0; + ELF_File * exec = NULL; + if (!file->debug_info_file) continue; + exec = open_memory_region_file(r, &error); + if (exec == NULL) continue; + if (exec->debug_info_file_name == NULL) continue; + if (strcmp(exec->debug_info_file_name, file->name) != 0) continue; + } + if (r->sect_name == NULL) { + unsigned j; + if (file->pheader_cnt == 0 && file->type == ET_EXEC) return addr; + for (j = 0; j < file->pheader_cnt; j++) { + U8_T offs; + ELF_PHeader * p = file->pheaders + j; + if (p->type != PT_LOAD) continue; + if (addr < p->address || addr >= p->address + p->mem_size) continue; + if (r->flags) { + if ((p->flags & PF_R) && !(r->flags & MM_FLAG_R)) continue; + if ((p->flags & PF_W) && !(r->flags & MM_FLAG_W)) continue; + if ((p->flags & PF_X) && !(r->flags & MM_FLAG_X)) continue; + } + offs = addr - p->address + p->offset; + if (offs < r->file_offs || offs >= r->file_offs + r->size) continue; + return (ContextAddress)(offs - r->file_offs + r->addr); + } + } + else if (sec != NULL && strcmp(sec->name, r->sect_name) == 0) { + return (ContextAddress)(addr - sec->addr + r->addr); + } + } + return 0; +} + +ContextAddress elf_map_to_link_time_address(Context * ctx, ContextAddress addr, ELF_File ** file, ELF_Section ** sec) { + unsigned i; + + if (get_map(ctx, addr, addr, &elf_map) < 0) return 0; + for (i = 0; i < elf_map.region_cnt; i++) { + MemoryRegion * r = elf_map.regions + i; + ELF_File * f = NULL; + assert(r->addr <= addr); + assert(r->addr + r->size > addr); + f = open_memory_region_file(r, NULL); + if (f == NULL) continue; + if (r->sect_name == NULL) { + unsigned j; + if (f->pheader_cnt == 0 && f->type == ET_EXEC) { + *file = f; + for (j = 1; j < f->section_cnt; j++) { + ELF_Section * s = f->sections + j; + if ((s->flags & SHF_ALLOC) == 0) continue; + if (s->addr <= addr && s->addr + s->size > addr) { + *sec = s; + return addr; + } + } + *sec = NULL; + return addr; + } + for (j = 0; j < f->pheader_cnt; j++) { + U8_T offs = addr - r->addr + r->file_offs; + ELF_PHeader * p = f->pheaders + j; + if (p->type != PT_LOAD) continue; + if (offs < p->offset || offs >= p->offset + p->mem_size) continue; + if (r->flags) { + if ((p->flags & PF_R) && !(r->flags & MM_FLAG_R)) continue; + if ((p->flags & PF_W) && !(r->flags & MM_FLAG_W)) continue; + if ((p->flags & PF_X) && !(r->flags & MM_FLAG_X)) continue; + } + *file = f; + addr = (ContextAddress)(offs - p->offset + p->address); + for (j = 1; j < f->section_cnt; j++) { + ELF_Section * s = f->sections + j; + if ((s->flags & SHF_ALLOC) == 0) continue; + if (s->addr + s->size <= p->address) continue; + if (s->addr >= p->address + p->mem_size) continue; + if (s->addr <= addr && s->addr + s->size > addr) { + *sec = s; + return addr; + } + } + *sec = NULL; + return addr; + } + } + else { + unsigned j; + for (j = 1; j < f->section_cnt; j++) { + ELF_Section * s = f->sections + j; + if (strcmp(s->name, r->sect_name) == 0) { + *file = f; + *sec = s; + return (ContextAddress)(addr - r->addr + s->addr); + } + } + } + } + return 0; +} + +static int get_dynamic_tag(Context * ctx, ELF_File * file, int tag, ContextAddress * addr) { + unsigned i, j; + + for (i = 1; i < file->section_cnt; i++) { + ELF_Section * sec = file->sections + i; + if (sec->size == 0) continue; + if (sec->name == NULL) continue; + if (strcmp(sec->name, ".dynamic") == 0) { + ContextAddress sec_addr = elf_map_to_run_time_address(ctx, file, sec, (ContextAddress)sec->addr); + if (elf_load(sec) < 0) return -1; + if (file->elf64) { + unsigned cnt = (unsigned)(sec->size / sizeof(Elf64_Dyn)); + for (j = 0; j < cnt; j++) { + Elf64_Dyn dyn = *((Elf64_Dyn *)sec->data + j); + if (file->byte_swap) SWAP(dyn.d_tag); + if (dyn.d_tag == DT_NULL) break; + if (dyn.d_tag == tag) { + if (context_read_mem(ctx, sec_addr + j * sizeof(dyn), &dyn, sizeof(dyn)) < 0) return -1; + if (file->byte_swap) { + SWAP(dyn.d_tag); + SWAP(dyn.d_un.d_ptr); + } + if (dyn.d_tag != tag) continue; + if (addr != NULL) *addr = (ContextAddress)dyn.d_un.d_ptr; + return 0; + } + } + } + else { + unsigned cnt = (unsigned)(sec->size / sizeof(Elf32_Dyn)); + for (j = 0; j < cnt; j++) { + Elf32_Dyn dyn = *((Elf32_Dyn *)sec->data + j); + if (file->byte_swap) SWAP(dyn.d_tag); + if (dyn.d_tag == DT_NULL) break; + if (dyn.d_tag == tag) { + if (context_read_mem(ctx, sec_addr + j * sizeof(dyn), &dyn, sizeof(dyn)) < 0) return -1; + if (file->byte_swap) { + SWAP(dyn.d_tag); + SWAP(dyn.d_un.d_ptr); + } + if (dyn.d_tag != tag) continue; + if (addr != NULL) *addr = (ContextAddress)dyn.d_un.d_ptr; + return 0; + } + } + } + } + } + errno = ENOENT; + return -1; +} + +static int sym_name_cmp(const char * x, const char * y) { + while (*x && *x == *y) { + x++; + y++; + } + if (*x == 0 && *y == 0) return 0; + if (*x == '@' && *(x + 1) == '@' && *y == 0) return 0; + if (*x < *y) return -1; + return 1; +} + +static int get_global_symbol_address(Context * ctx, ELF_File * file, const char * name, ContextAddress * addr) { + unsigned i, j; + + for (i = 1; i < file->section_cnt; i++) { + ELF_Section * sec = file->sections + i; + if (sec->size == 0) continue; + if (sec->type == SHT_SYMTAB) { + ELF_Section * str = NULL; + if (sec->link == 0 || sec->link >= file->section_cnt) { + errno = EINVAL; + return -1; + } + str = file->sections + sec->link; + if (elf_load(sec) < 0) return -1; + if (elf_load(str) < 0) return -1; + if (file->elf64) { + unsigned cnt = (unsigned)(sec->size / sizeof(Elf64_Sym)); + for (j = 0; j < cnt; j++) { + Elf64_Sym sym = *((Elf64_Sym *)sec->data + j); + if (ELF64_ST_BIND(sym.st_info) != STB_GLOBAL) continue; + if (file->byte_swap) SWAP(sym.st_name); + if (sym_name_cmp((char *)str->data + sym.st_name, name) != 0) continue; + switch (ELF64_ST_TYPE(sym.st_info)) { + case STT_OBJECT: + case STT_FUNC: + if (file->byte_swap) SWAP(sym.st_value); + *addr = elf_map_to_run_time_address(ctx, file, NULL, (ContextAddress)sym.st_value); + if (*addr != 0) return 0; + } + } + } + else { + unsigned cnt = (unsigned)(sec->size / sizeof(Elf32_Sym)); + for (j = 0; j < cnt; j++) { + Elf32_Sym sym = *((Elf32_Sym *)sec->data + j); + if (ELF32_ST_BIND(sym.st_info) != STB_GLOBAL) continue; + if (file->byte_swap) SWAP(sym.st_name); + if (sym_name_cmp((char *)str->data + sym.st_name, name) != 0) continue; + switch (ELF32_ST_TYPE(sym.st_info)) { + case STT_OBJECT: + case STT_FUNC: + if (file->byte_swap) SWAP(sym.st_value); + *addr = elf_map_to_run_time_address(ctx, file, NULL, (ContextAddress)sym.st_value); + if (*addr != 0) return 0; + } + } + } + } + } + errno = ENOENT; + return -1; +} + +int elf_read_memory_word(Context * ctx, ELF_File * file, ContextAddress addr, ContextAddress * word) { + size_t size = file->elf64 ? 8 : 4; + size_t i = 0; + U8_T n = 0; + U1_T buf[8]; + + if (ctx->mem_access == 0) ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); + if (context_read_mem(ctx, addr, buf, size) < 0) return -1; + for (i = 0; i < size; i++) { + n = (n << 8) | buf[file->big_endian ? i : size - i - 1]; + } + *word = (ContextAddress)n; + return 0; +} + +ContextAddress elf_get_debug_structure_address(Context * ctx, ELF_File ** file_ptr) { + ELF_File * file = NULL; + ContextAddress addr = 0; + + for (file = elf_list_first(ctx, 0, ~(ContextAddress)0); file != NULL; file = elf_list_next(ctx)) { + if (file->type != ET_EXEC) continue; + if (file_ptr != NULL) *file_ptr = file; +#ifdef DT_MIPS_RLD_MAP + if (get_dynamic_tag(ctx, file, DT_MIPS_RLD_MAP, &addr) == 0) { + if (elf_read_memory_word(ctx, file, addr, &addr) < 0) continue; + break; + } +#endif + if (get_dynamic_tag(ctx, file, DT_DEBUG, &addr) == 0) break; + if (get_global_symbol_address(ctx, file, "_r_debug", &addr) == 0) break; + } + elf_list_done(ctx); + + return addr; +} + +#endif /* ENABLE_DebugContext */ + + +/************************ ELF symbol tables *****************************************/ + +unsigned calc_symbol_name_hash(const char * s) { + unsigned h = 0; + while (*s) { + unsigned g; + if (s[0] == '@' && s[1] == '@') break; + h = (h << 4) + (unsigned char)*s++; + g = h & 0xf0000000; + if (g) h ^= g >> 24; + h &= ~g; + } + return h % SYM_HASH_SIZE; +} + +int cmp_symbol_names(const char * x, const char * y) { + while (*x && *x == *y) { + x++; + y++; + } + if (*x == 0 && *y == '@' && y[1] == '@') return 0; + if (*y == 0 && *x == '@' && x[1] == '@') return 0; + if (*x < *y) return -1; + if (*x > *y) return +1; + return 0; +} + +void unpack_elf_symbol_info(ELF_Section * sym_sec, U4_T index, ELF_SymbolInfo * info) { + ELF_File * file = sym_sec->file; + ELF_Section * str_sec = NULL; + char * str_pool = NULL; + size_t str_pool_size = 0; + memset(info, 0, sizeof(ELF_SymbolInfo)); + if (index >= sym_sec->sym_count) str_exception(ERR_INV_FORMAT, "Invalid ELF symbol index"); + if (sym_sec->link == 0 || sym_sec->link >= file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid symbol section"); + str_sec = file->sections + sym_sec->link; + if (elf_load(sym_sec) < 0) exception(errno); + if (elf_load(str_sec) < 0) exception(errno); + str_pool = (char *)str_sec->data; + str_pool_size = (size_t)str_sec->size; + info->sym_section = sym_sec; + info->sym_index = index; + if (file->elf64) { + Elf64_Sym s = ((Elf64_Sym *)sym_sec->data)[index]; + if (file->byte_swap) { + SWAP(s.st_name); + SWAP(s.st_shndx); + SWAP(s.st_size); + SWAP(s.st_value); + } + info->section_index = s.st_shndx; + if (s.st_shndx > 0 && s.st_shndx < file->section_cnt) { + info->section = file->sections + s.st_shndx; + } + if (s.st_name > 0) { + if (s.st_name >= str_pool_size) str_exception(ERR_INV_FORMAT, "Invalid ELF string pool index"); + info->name = str_pool + s.st_name; + } + info->bind = ELF64_ST_BIND(s.st_info); + info->type = ELF64_ST_TYPE(s.st_info); + info->value = s.st_value; + info->size = s.st_size; + } + else { + Elf32_Sym s = ((Elf32_Sym *)sym_sec->data)[index]; + if (file->byte_swap) { + SWAP(s.st_name); + SWAP(s.st_shndx); + SWAP(s.st_size); + SWAP(s.st_value); + } + info->section_index = s.st_shndx; + if (s.st_shndx > 0 && s.st_shndx < file->section_cnt) { + info->section = file->sections + s.st_shndx; + } + if (s.st_name > 0) { + if (s.st_name >= str_pool_size) str_exception(ERR_INV_FORMAT, "Invalid ELF string pool index"); + info->name = str_pool + s.st_name; + } + info->bind = ELF32_ST_BIND(s.st_info); + info->type = ELF32_ST_TYPE(s.st_info); + info->value = s.st_value; + info->size = s.st_size; + } +} + +static int section_symbol_comparator(const void * x, const void * y) { + ELF_SecSymbol * rx = (ELF_SecSymbol *)x; + ELF_SecSymbol * ry = (ELF_SecSymbol *)y; + if (rx->address < ry->address) return -1; + if (rx->address > ry->address) return +1; + return 0; +} + +static void create_symbol_addr_search_index(ELF_Section * sec) { + ELF_File * file = sec->file; + int elf64 = file->elf64; + int swap = file->byte_swap; + int rel = file->type == ET_REL; + unsigned m = 0; + + sec->sym_addr_max = (unsigned)(sec->size / 16) + 16; + sec->sym_addr_table = (ELF_SecSymbol *)loc_alloc(sec->sym_addr_max * sizeof(ELF_SecSymbol)); + + for (m = 1; m < file->section_cnt; m++) { + unsigned n = 1; + ELF_Section * tbl = file->sections + m; + if (tbl->sym_count == 0) continue; + if (elf_load(tbl) < 0) exception(errno); + while (n < tbl->sym_count) { + int add = 0; + U8_T addr = 0; + if (elf64) { + Elf64_Sym s = ((Elf64_Sym *)tbl->data)[n]; + if (swap) SWAP(s.st_shndx); + if (s.st_shndx == sec->index) { + if (swap) SWAP(s.st_value); + addr = s.st_value; + if (rel) addr += sec->addr; + add = 1; + } + } + else { + Elf32_Sym s = ((Elf32_Sym *)tbl->data)[n]; + if (swap) SWAP(s.st_shndx); + if (s.st_shndx == sec->index) { + if (swap) SWAP(s.st_value); + addr = s.st_value; + if (rel) addr += sec->addr; + add = 1; + } + } + if (add) { + ELF_SecSymbol * s = NULL; + if (sec->sym_addr_cnt >= sec->sym_addr_max) { + sec->sym_addr_max = sec->sym_addr_max * 3 / 2; + sec->sym_addr_table = (ELF_SecSymbol *)loc_realloc(sec->sym_addr_table, sec->sym_addr_max * sizeof(ELF_SecSymbol)); + } + s = sec->sym_addr_table + sec->sym_addr_cnt++; + s->address = addr; + s->section = tbl; + s->index = n; + } + n++; + } + } + + qsort(sec->sym_addr_table, sec->sym_addr_cnt, sizeof(ELF_SecSymbol), section_symbol_comparator); +} + +void elf_find_symbol_by_address(ELF_Section * sec, ContextAddress addr, ELF_SymbolInfo * sym_info) { + unsigned l = 0; + unsigned h = 0; + memset(sym_info, 0, sizeof(ELF_SymbolInfo)); + if (sec == NULL || addr < sec->addr) return; + if (sec->sym_addr_table == NULL) create_symbol_addr_search_index(sec); + h = sec->sym_addr_cnt; + while (l < h) { + unsigned k = (h + l) / 2; + ELF_SecSymbol * info = sec->sym_addr_table + k; + if (info->address > addr) { + h = k; + } + else { + ContextAddress next = k < sec->sym_addr_cnt - 1 ? + (info + 1)->address : sec->addr + sec->size; + assert(next >= info->address); + if (next <= addr) { + l = k + 1; + } + else { + unpack_elf_symbol_info(info->section, info->index, sym_info); + assert(sym_info->section == sec); + sym_info->addr_index = k; + return; + } + } + } +} + +void elf_prev_symbol_by_address(ELF_SymbolInfo * sym_info) { + if (sym_info->section != NULL && sym_info->addr_index > 0) { + U4_T index = sym_info->addr_index - 1; + ELF_SecSymbol * info = sym_info->section->sym_addr_table + index; + unpack_elf_symbol_info(info->section, info->index, sym_info); + sym_info->addr_index = index; + } + else { + memset(sym_info, 0, sizeof(ELF_SymbolInfo)); + } +} + +void elf_next_symbol_by_address(ELF_SymbolInfo * sym_info) { + if (sym_info->section != NULL && sym_info->addr_index + 1 < sym_info->section->sym_addr_cnt) { + U4_T index = sym_info->addr_index + 1; + ELF_SecSymbol * info = sym_info->section->sym_addr_table + index; + unpack_elf_symbol_info(info->section, info->index, sym_info); + sym_info->addr_index = index; + } + else { + memset(sym_info, 0, sizeof(ELF_SymbolInfo)); + } +} + +void ini_elf(void) { +} + +#endif /* ENABLE_ELF */ |