diff options
author | Andy Wingo <wingo@pobox.com> | 2018-07-04 14:32:45 +0200 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2018-07-20 11:42:13 +0200 |
commit | e95f15c932650783f34253e396216c66050905f0 (patch) | |
tree | ef728f2fedc0c7abf5a3cb776ff343037dcebb20 | |
parent | 80ba0e2cb110983eeafe66a0ea0bf5dba8e25226 (diff) | |
download | guile-e95f15c932650783f34253e396216c66050905f0.tar.gz |
Add support for reading ELF symbol table from C
* libguile/loader.c (scm_i_program_address_range): New function.
Currently unused, will wire up once we correct the compiler to emit
symbols whose values are relative to the base addr. Will be used by
JIT so it needs to be fast.
(register_elf): Rework to be less verbose, and record symtab/strtab.
(struct mapped_elf_image): Add symtab and strtab members.
(load_thunk_from_memory, process_dynamic_segment): Parse out DT_SYMTAB
and DT_STRTAB.
(Elf_Sym): New definition.
-rw-r--r-- | libguile/loader.c | 123 | ||||
-rw-r--r-- | libguile/loader.h | 7 |
2 files changed, 92 insertions, 38 deletions
diff --git a/libguile/loader.c b/libguile/loader.c index b37721c52..8f2504073 100644 --- a/libguile/loader.c +++ b/libguile/loader.c @@ -61,6 +61,7 @@ #define ELFCLASS ELFCLASS32 #define Elf_Phdr Elf32_Phdr #define Elf_Dyn Elf32_Dyn +#define Elf_Sym Elf32_Sym #elif SIZEOF_UINTPTR_T == 8 #define Elf_Half Elf64_Half #define Elf_Word Elf64_Word @@ -68,6 +69,7 @@ #define ELFCLASS ELFCLASS64 #define Elf_Phdr Elf64_Phdr #define Elf_Dyn Elf64_Dyn +#define Elf_Sym Elf64_Sym #else #error #endif @@ -90,7 +92,8 @@ /* The page size. */ static size_t page_size; -static void register_elf (char *data, size_t len, char *frame_maps); +static void register_elf (char *data, size_t len, char *frame_maps, + char *symtab, char *strtab); enum bytecode_kind { @@ -258,12 +261,14 @@ segment_flags_to_prot (Elf_Word flags) static char* process_dynamic_segment (char *base, Elf_Phdr *dyn_phdr, - SCM *init_out, SCM *entry_out, char **frame_maps_out) + SCM *init_out, SCM *entry_out, char **frame_maps_out, + char **symtab_out, char **strtab_out) { char *dyn_addr = base + dyn_phdr->p_vaddr; Elf_Dyn *dyn = (Elf_Dyn *) dyn_addr; size_t i, dyn_size = dyn_phdr->p_memsz / sizeof (Elf_Dyn); char *init = 0, *gc_root = 0, *entry = 0, *frame_maps = 0; + char *symtab = 0, *strtab = 0; ptrdiff_t gc_root_size = 0; enum bytecode_kind bytecode_kind = BYTECODE_KIND_NONE; @@ -279,6 +284,12 @@ process_dynamic_segment (char *base, Elf_Phdr *dyn_phdr, return "duplicate DT_INIT"; init = base + dyn[i].d_un.d_val; break; + case DT_SYMTAB: + symtab = base + dyn[i].d_un.d_val; + break; + case DT_STRTAB: + strtab = base + dyn[i].d_un.d_val; + break; case DT_GUILE_GC_ROOT: if (gc_root) return "duplicate DT_GUILE_GC_ROOT"; @@ -344,6 +355,8 @@ process_dynamic_segment (char *base, Elf_Phdr *dyn_phdr, *init_out = init ? pointer_to_procedure (bytecode_kind, init) : SCM_BOOL_F; *entry_out = pointer_to_procedure (bytecode_kind, entry); *frame_maps_out = frame_maps; + *symtab_out = symtab; + *strtab_out = strtab; return NULL; } @@ -361,7 +374,7 @@ load_thunk_from_memory (char *data, size_t len, int is_read_only) int i; int dynamic_segment = -1; SCM init = SCM_BOOL_F, entry = SCM_BOOL_F; - char *frame_maps = 0; + char *frame_maps = 0, *symtab = 0, *strtab = 0; errno = 0; @@ -465,13 +478,14 @@ load_thunk_from_memory (char *data, size_t len, int is_read_only) } if ((err_msg = process_dynamic_segment (data, &ph[dynamic_segment], - &init, &entry, &frame_maps))) + &init, &entry, &frame_maps, + &symtab, &strtab))) goto cleanup; if (scm_is_true (init)) scm_call_0 (init); - register_elf (data, len, frame_maps); + register_elf (data, len, frame_maps, symtab, strtab); /* Finally! Return the thunk. */ return entry; @@ -602,9 +616,11 @@ SCM_DEFINE (scm_load_thunk_from_memory, "load-thunk-from-memory", 1, 0, 0, struct mapped_elf_image { - char *start; - char *end; - char *frame_maps; + const char *start; + const char *end; + const char *frame_maps; + const char *symtab; + const char *strtab; }; static struct mapped_elf_image *mapped_elf_images = NULL; @@ -612,7 +628,7 @@ static size_t mapped_elf_images_count = 0; static size_t mapped_elf_images_allocated = 0; static size_t -find_mapped_elf_insertion_index (char *ptr) +find_mapped_elf_insertion_index (const char *ptr) { /* "mapped_elf_images_count" must never be dereferenced. */ size_t start = 0, end = mapped_elf_images_count; @@ -631,7 +647,8 @@ find_mapped_elf_insertion_index (char *ptr) } static void -register_elf (char *data, size_t len, char *frame_maps) +register_elf (char *data, size_t len, char *frame_maps, char *symtab, + char *strtab) { scm_i_pthread_mutex_lock (&scm_i_misc_mutex); { @@ -639,7 +656,7 @@ register_elf (char *data, size_t len, char *frame_maps) if (mapped_elf_images_count == mapped_elf_images_allocated) { struct mapped_elf_image *prev; - size_t n; + size_t old_size, new_size; if (mapped_elf_images_allocated) mapped_elf_images_allocated *= 2; @@ -647,42 +664,33 @@ register_elf (char *data, size_t len, char *frame_maps) mapped_elf_images_allocated = 16; prev = mapped_elf_images; - mapped_elf_images = - scm_gc_malloc_pointerless (sizeof (*mapped_elf_images) - * mapped_elf_images_allocated, - "mapped elf images"); + old_size = mapped_elf_images_count * sizeof (*mapped_elf_images); + new_size = mapped_elf_images_allocated * sizeof (*mapped_elf_images); + mapped_elf_images = scm_gc_malloc_pointerless (new_size, "mapped elf"); - for (n = 0; n < mapped_elf_images_count; n++) - { - mapped_elf_images[n].start = prev[n].start; - mapped_elf_images[n].end = prev[n].end; - mapped_elf_images[n].frame_maps = prev[n].frame_maps; - } + memcpy (mapped_elf_images, prev, old_size); } { - size_t end; size_t n = find_mapped_elf_insertion_index (data); - for (end = mapped_elf_images_count; n < end; end--) - { - const struct mapped_elf_image *prev = &mapped_elf_images[end - 1]; - mapped_elf_images[end].start = prev->start; - mapped_elf_images[end].end = prev->end; - mapped_elf_images[end].frame_maps = prev->frame_maps; - } + memmove (&mapped_elf_images[n+1], &mapped_elf_images[n], + (mapped_elf_images_count - n) * sizeof (*mapped_elf_images)); + mapped_elf_images_count++; mapped_elf_images[n].start = data; mapped_elf_images[n].end = data + len; mapped_elf_images[n].frame_maps = frame_maps; + mapped_elf_images[n].symtab = symtab; + mapped_elf_images[n].strtab = strtab; } } scm_i_pthread_mutex_unlock (&scm_i_misc_mutex); } static struct mapped_elf_image * -find_mapped_elf_image_unlocked (char *ptr) +find_mapped_elf_image_unlocked (const char *ptr) { size_t n = find_mapped_elf_insertion_index ((char *) ptr); @@ -695,7 +703,7 @@ find_mapped_elf_image_unlocked (char *ptr) } static int -find_mapped_elf_image (char *ptr, struct mapped_elf_image *image) +find_mapped_elf_image (const char *ptr, struct mapped_elf_image *image) { int result; @@ -771,9 +779,9 @@ const uint8_t * scm_find_slot_map_unlocked (const uint32_t *ip) { struct mapped_elf_image *image; - char *base; - struct frame_map_prefix *prefix; - struct frame_map_header *headers; + const char *base; + const struct frame_map_prefix *prefix; + const struct frame_map_header *headers; uintptr_t addr = (uintptr_t) ip; size_t start, end; @@ -782,8 +790,8 @@ scm_find_slot_map_unlocked (const uint32_t *ip) return NULL; base = image->frame_maps; - prefix = (struct frame_map_prefix *) base; - headers = (struct frame_map_header *) (base + sizeof (*prefix)); + prefix = (const struct frame_map_prefix *) base; + headers = (const struct frame_map_header *) (base + sizeof (*prefix)); if (addr < ((uintptr_t) image->start) + prefix->text_offset) return NULL; @@ -810,6 +818,49 @@ scm_find_slot_map_unlocked (const uint32_t *ip) return NULL; } +int +scm_i_program_address_range (const char *code, const char **start, + const char **end) +{ + struct mapped_elf_image image; + ptrdiff_t size; + const Elf_Sym *symtab, *sym; + ptrdiff_t lo, hi, offset; + + if (!find_mapped_elf_image (code, &image)) + return 0; + + if (!image.symtab || !image.strtab) + return 0; + + /* The compiler will put the strtab directly after the symtab, which + lets us know the symtab size without traversing the section + table. It's hacky but it's what glibc does. */ + if (image.symtab >= image.strtab) + abort (); + + size = (image.strtab - image.symtab) / sizeof (Elf_Sym); + symtab = (const Elf_Sym *) image.symtab; + lo = 0, hi = size, offset = code - image.start; + while (lo < hi) + { + ptrdiff_t test = (lo + hi) / 2; + sym = symtab + test; + if (offset < sym->st_value) + hi = test; + else if (offset < sym->st_value + sym->st_size) + { + *start = image.start + sym->st_value; + *end = *start + sym->st_size; + return 1; + } + else + lo = test + 1; + } + + return 0; +} + void scm_bootstrap_loader (void) diff --git a/libguile/loader.h b/libguile/loader.h index d9f632d1e..54ec9f798 100644 --- a/libguile/loader.h +++ b/libguile/loader.h @@ -55,8 +55,11 @@ SCM_API SCM scm_load_thunk_from_file (SCM filename); SCM_API SCM scm_load_thunk_from_memory (SCM bv); -SCM_INTERNAL const uint8_t * -scm_find_slot_map_unlocked (const uint32_t *ip); +SCM_INTERNAL const uint8_t * scm_find_slot_map_unlocked (const uint32_t *ip); + +SCM_INTERNAL int scm_i_program_address_range (const char *code, + const char **start, + const char **end); SCM_INTERNAL void scm_bootstrap_loader (void); SCM_INTERNAL void scm_init_loader (void); |