diff --git a/sys/ia64/ia64/elf_machdep.c b/sys/ia64/ia64/elf_machdep.c index ea8695fda0a..c4a4938f5e2 100644 --- a/sys/ia64/ia64/elf_machdep.c +++ b/sys/ia64/ia64/elf_machdep.c @@ -218,15 +218,44 @@ elf_reloc(linker_file_t lf, const void *data, int type) } int -elf_cpu_load_file(linker_file_t lf __unused) +elf_cpu_load_file(linker_file_t lf) { + Elf_Ehdr *hdr; + Elf_Phdr *ph, *phlim; + Elf_Addr reloc, vaddr; + + hdr = (Elf_Ehdr *)(lf->address); + if (!IS_ELF(*hdr)) { + printf("Missing or corrupted ELF header at %p\n", hdr); + return (EFTYPE); + } + + /* + * Iterate over the segments and register the unwind table if + * we come across it. + */ + ph = (Elf_Phdr *)(lf->address + hdr->e_phoff); + phlim = ph + hdr->e_phnum; + reloc = ~0ULL; + while (ph < phlim) { + if (ph->p_type == PT_LOAD && reloc == ~0ULL) + reloc = (Elf_Addr)lf->address - ph->p_vaddr; + + if (ph->p_type == PT_IA_64_UNWIND) { + vaddr = ph->p_vaddr + reloc; + ia64_add_unwind_table((vm_offset_t)lf->address, vaddr, + vaddr + ph->p_memsz); + } + ++ph; + } return (0); } int -elf_cpu_unload_file(linker_file_t lf __unused) +elf_cpu_unload_file(linker_file_t lf) { + ia64_delete_unwind_table((vm_offset_t)lf->address); return (0); } diff --git a/sys/ia64/ia64/machdep.c b/sys/ia64/ia64/machdep.c index 88083a3b099..d3f5363235e 100644 --- a/sys/ia64/ia64/machdep.c +++ b/sys/ia64/ia64/machdep.c @@ -109,8 +109,6 @@ struct user *proc0uarea; vm_offset_t proc0kstack; extern u_int64_t kernel_text[], _end[]; -extern u_int64_t _ia64_unwind_start[]; -extern u_int64_t _ia64_unwind_end[]; FPSWA_INTERFACE *fpswa_interface; @@ -303,18 +301,6 @@ identifycpu(void) "\001LB"); } -static void -add_kernel_unwind_tables(void *arg) -{ - /* - * Register the kernel's unwind table. - */ - ia64_add_unwind_table(kernel_text, - _ia64_unwind_start, - _ia64_unwind_end); -} -SYSINIT(unwind, SI_SUB_KMEM, SI_ORDER_ANY, add_kernel_unwind_tables, 0); - void map_pal_code(void) { diff --git a/sys/ia64/ia64/unwind.c b/sys/ia64/ia64/unwind.c index 4c0e4def992..2da70419534 100644 --- a/sys/ia64/ia64/unwind.c +++ b/sys/ia64/ia64/unwind.c @@ -122,42 +122,23 @@ static struct ia64_unwind_table_list ia64_unwind_tables; static struct ia64_unwind_state ia64_unwind_state_static[MAX_UNWIND_STATES]; static LIST_HEAD(ia64_unwind_state_list, ia64_unwind_state) ia64_unwind_states; -struct ia64_unwind_table * -ia64_add_unwind_table(u_int64_t *base, u_int64_t *start, u_int64_t *end) +static void +ia64_initialise_unwind(void *arg __unused) { - struct ia64_unwind_table *ut; + int i; - if (!ia64_unwind_initialised) { - int i; - LIST_INIT(&ia64_unwind_tables); - LIST_INIT(&ia64_unwind_states); - for (i = 0; i < MAX_UNWIND_STATES; i++) { - LIST_INSERT_HEAD(&ia64_unwind_states, - &ia64_unwind_state_static[i], - us_link); - } - ia64_unwind_initialised = 1; + KASSERT(!ia64_unwind_initialised, ("foo")); + + LIST_INIT(&ia64_unwind_tables); + LIST_INIT(&ia64_unwind_states); + for (i = 0; i < MAX_UNWIND_STATES; i++) { + LIST_INSERT_HEAD(&ia64_unwind_states, + &ia64_unwind_state_static[i], us_link); } - ut = malloc(sizeof(struct ia64_unwind_table), M_UNWIND, M_NOWAIT); - if (!ut) - return 0; - - ut->ut_base = (u_int64_t) base; - ut->ut_start = (struct ia64_unwind_table_entry *) start; - ut->ut_end = (struct ia64_unwind_table_entry *) end; - ut->ut_limit = (u_int64_t) base + ut->ut_end[-1].ue_end; - LIST_INSERT_HEAD(&ia64_unwind_tables, ut, ut_link); - - return ut; -} - -void -ia64_free_unwind_table(struct ia64_unwind_table *ut) -{ - LIST_REMOVE(ut, ut_link); - free(ut, M_UNWIND); + ia64_unwind_initialised = 1; } +SYSINIT(unwind, SI_SUB_KMEM, SI_ORDER_ANY, ia64_initialise_unwind, 0); static struct ia64_unwind_table * find_table(u_int64_t ip) @@ -198,6 +179,46 @@ find_entry(struct ia64_unwind_table *ut, u_int64_t ip) return 0; } +int +ia64_add_unwind_table(vm_offset_t base, vm_offset_t start, vm_offset_t end) +{ + struct ia64_unwind_table *ut; + + KASSERT(ia64_unwind_initialised, ("foo")); + + ut = malloc(sizeof(struct ia64_unwind_table), M_UNWIND, M_NOWAIT); + if (ut == NULL) + return (ENOMEM); + + ut->ut_base = base; + ut->ut_start = (struct ia64_unwind_table_entry*)start; + ut->ut_end = (struct ia64_unwind_table_entry*)end; + ut->ut_limit = base + ut->ut_end[-1].ue_end; + LIST_INSERT_HEAD(&ia64_unwind_tables, ut, ut_link); + + if (bootverbose) + printf("UNWIND: table added: base=%lx, start=%lx, end=%lx\n", + base, start, end); + + return (0); +} + +void +ia64_delete_unwind_table(vm_offset_t base) +{ + struct ia64_unwind_table *ut; + + KASSERT(ia64_unwind_initialised, ("foo")); + + ut = find_table(base); + if (ut != NULL) { + LIST_REMOVE(ut, ut_link); + free(ut, M_UNWIND); + if (bootverbose) + printf("UNWIND: table removed: base=%lx\n", base); + } +} + struct ia64_unwind_state * ia64_create_unwind_state(struct trapframe *framep) { diff --git a/sys/ia64/include/unwind.h b/sys/ia64/include/unwind.h index cc00b617e4b..3509c054ea6 100644 --- a/sys/ia64/include/unwind.h +++ b/sys/ia64/include/unwind.h @@ -26,12 +26,9 @@ * $FreeBSD$ */ -struct ia64_unwind_table; +int ia64_add_unwind_table(vm_offset_t, vm_offset_t, vm_offset_t); +void ia64_delete_unwind_table(vm_offset_t); -struct ia64_unwind_table *ia64_add_unwind_table(u_int64_t *base, - u_int64_t *start, - u_int64_t *end); -void ia64_free_unwind_table(struct ia64_unwind_table *ut); struct ia64_unwind_state *ia64_create_unwind_state(struct trapframe *framep); void ia64_free_unwind_state(struct ia64_unwind_state *us); u_int64_t ia64_unwind_state_get_ip(struct ia64_unwind_state *us);