From 0f33053e1ebb5931f9dfbb6268aec57b7ce003da Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 20 Jan 2010 18:05:26 +0100 Subject: [PATCH] use eh_frame_hdr table to speed up unwinding * runtime/sym.h (_stp_module): add unwind_hdr_addr member. * runtime/unwind.c (read_ptr_sect): Modification of read_pointer that also handles offsets from text or "data" sections. (read_pointer): Use it. (_stp_search_unwind_hdr): Use read_ptr_sect to calculate values that are relative to the .eh_frame_hdr section. (unwind_frame): Search the frame header if this is an eh frame. * translate.cxx (get_unwind_data): Get the eh_frame_hdr too. (dump_unwindsyms): Write out eh_frame_hdr stuff. --- runtime/sym.h | 1 + runtime/unwind.c | 58 ++++++++++++++++++++++++++----------- translate.cxx | 74 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 105 insertions(+), 28 deletions(-) diff --git a/runtime/sym.h b/runtime/sym.h index ce6ab736e..e70228777 100644 --- a/runtime/sym.h +++ b/runtime/sym.h @@ -49,6 +49,7 @@ struct _stp_module { uint32_t eh_frame_len; uint32_t unwind_hdr_len; unsigned long eh_frame_addr; /* Orig load address (offset) .eh_frame */ + unsigned long unwind_hdr_addr; /* same for .eh_frame_hdr */ /* build-id information */ unsigned char *build_id_bits; unsigned long build_id_offset; diff --git a/runtime/unwind.c b/runtime/unwind.c index c8e3580dd..f60ea017d 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -133,8 +133,11 @@ static const u32 *cie_for_fde(const u32 *fde, void *unwind_data, return cie; } -/* read an encoded pointer */ -static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrType) +/* read an encoded pointer and increment *pLoc past the end of the + * data read. */ +static unsigned long read_ptr_sect(const u8 **pLoc, const void *end, + signed ptrType, unsigned long textAddr, + unsigned long dataAddr) { unsigned long value = 0; union { @@ -194,6 +197,12 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrTy case DW_EH_PE_pcrel: value += (unsigned long)*pLoc; break; + case DW_EH_PE_textrel: + value += textAddr; + break; + case DW_EH_PE_datarel: + value += dataAddr; + break; default: return 0; } @@ -205,6 +214,11 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrTy return value; } +static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrType) +{ + return read_ptr_sect(pLoc, end, ptrType, 0, 0); +} + static signed fde_pointer_type(const u32 *cie, void *unwind_data, uint32_t table_len) { @@ -535,7 +549,6 @@ adjustStartLoc (unsigned long startLoc, struct task_struct *tsk, /* If we previously created an unwind header, then use it now to binary search */ /* for the FDE corresponding to pc. XXX FIXME not currently supported. */ - static u32 *_stp_search_unwind_hdr(unsigned long pc, struct task_struct *tsk, struct _stp_module *m, struct _stp_section *s) @@ -544,6 +557,7 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, struct task_struct *tsk, unsigned long startLoc; u32 *fde = NULL; unsigned num, tableSize, t2; + unsigned long eh_hdr_addr = m->unwind_hdr_addr; if (hdr == NULL || hdr[0] != 1) return NULL; @@ -570,21 +584,29 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, struct task_struct *tsk, } ptr = hdr + 4; end = hdr + m->unwind_hdr_len; - - if (read_pointer(&ptr, end, hdr[1]) != (unsigned long)m->debug_frame) { - dbug_unwind(1, "eh_frame_ptr not valid"); + { + // XXX Can the header validity be checked just once? + unsigned long eh = read_ptr_sect(&ptr, end, hdr[1], 0, + eh_hdr_addr); + if ((hdr[1] & DW_EH_PE_ADJUST) == DW_EH_PE_pcrel) + eh = eh - (unsigned long)hdr + eh_hdr_addr; + if ((eh != (unsigned long)m->eh_frame_addr)) { + dbug_unwind(1, "eh_frame_ptr in eh_frame_hdr 0x%lx not valid; eh_frame_addr = 0x%lx", eh, (unsigned long)m->eh_frame_addr); return NULL; + } } - - num = read_pointer(&ptr, end, hdr[2]); - if (num == 0 || num != (end - ptr) / (2 * tableSize) || (end - ptr) % (2 * tableSize)) { - dbug_unwind(1, "Bad num=%d end-ptr=%ld 2*tableSize=%d", num, (long)(end - ptr), 2 * tableSize); + num = read_ptr_sect(&ptr, end, hdr[2], 0, eh_hdr_addr); + if (num == 0 || num != (end - ptr) / (2 * tableSize) + || (end - ptr) % (2 * tableSize)) { + dbug_unwind(1, "Bad num=%d end-ptr=%ld 2*tableSize=%d", + num, (long)(end - ptr), 2 * tableSize); return NULL; } do { const u8 *cur = ptr + (num / 2) * (2 * tableSize); - startLoc = read_pointer(&cur, cur + tableSize, hdr[3]); + startLoc = read_ptr_sect(&cur, cur + tableSize, hdr[3], 0, + eh_hdr_addr); startLoc = adjustStartLoc(startLoc, tsk, m, s, hdr[3], 1); if (pc < startLoc) num /= 2; @@ -594,8 +616,12 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, struct task_struct *tsk, } } while (startLoc && num > 1); - if (num == 1 && (startLoc = adjustStartLoc(read_pointer(&ptr, ptr + tableSize, hdr[3]), tsk, m, s, hdr[3], 1)) != 0 && pc >= startLoc) - fde = (void *)read_pointer(&ptr, ptr + tableSize, hdr[3]); + if (num == 1 + && (startLoc = adjustStartLoc(read_ptr_sect(&ptr, ptr + tableSize, hdr[3], 0, eh_hdr_addr), tsk, m, s, hdr[3], 1)) != 0 && pc >= startLoc) + fde = (u32*)(read_ptr_sect(&ptr, ptr + tableSize, hdr[3], + 0, eh_hdr_addr) + - m->eh_frame_addr + +(u8*)m->eh_frame); dbug_unwind(1, "returning fde=%lx startLoc=%lx", (unsigned long) fde, startLoc); return fde; @@ -610,7 +636,7 @@ static int unwind_frame(struct unwind_frame_info *frame, void *table, uint32_t table_len, int is_ehframe) { #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) - const u32 *fde, *cie = NULL; + const u32 *fde = NULL, *cie = NULL; const u8 *ptr = NULL, *end = NULL; unsigned long pc = UNW_PC(frame) - frame->call_frame; unsigned long tableSize, startLoc = 0, endLoc = 0, cfa; @@ -623,8 +649,8 @@ static int unwind_frame(struct unwind_frame_info *frame, dbug_unwind(1, "Module %s: frame_len=%d", m->name, table_len); goto err; } - - fde = _stp_search_unwind_hdr(pc, tsk, m, s); + if (is_ehframe) + fde = _stp_search_unwind_hdr(pc, tsk, m, s); dbug_unwind(1, "%s: fde=%lx\n", m->name, (unsigned long) fde); /* found the fde, now set startLoc and endLoc */ diff --git a/translate.cxx b/translate.cxx index 81b8bef51..157e2bb03 100644 --- a/translate.cxx +++ b/translate.cxx @@ -4498,7 +4498,9 @@ struct unwindsym_dump_context static void get_unwind_data (Dwfl_Module *m, void **debug_frame, void **eh_frame, size_t *debug_len, size_t *eh_len, - Dwarf_Addr *eh_addr) + Dwarf_Addr *eh_addr, + void **eh_frame_hdr, size_t *eh_frame_hdr_len, + Dwarf_Addr *eh_frame_hdr_addr) { Dwarf_Addr start, bias = 0; GElf_Ehdr *ehdr, ehdr_mem; @@ -4514,9 +4516,11 @@ static void get_unwind_data (Dwfl_Module *m, scn = NULL; while ((scn = elf_nextscn(elf, scn))) { + bool eh_frame_seen = false; + bool eh_frame_hdr_seen = false; shdr = gelf_getshdr(scn, &shdr_mem); - if (strcmp(elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name), - ".eh_frame") == 0) + const char* scn_name = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name); + if (!eh_frame_seen && strcmp(scn_name, ".eh_frame") == 0) { data = elf_rawdata(scn, NULL); *eh_frame = data->d_buf; @@ -4526,8 +4530,21 @@ static void get_unwind_data (Dwfl_Module *m, *eh_addr = shdr->sh_addr - start + bias; else *eh_addr = shdr->sh_addr; - break; + eh_frame_seen = true; } + else if (!eh_frame_hdr_seen && strcmp(scn_name, ".eh_frame_hdr") == 0) + { + data = elf_rawdata(scn, NULL); + *eh_frame_hdr = data->d_buf; + *eh_frame_hdr_len = data->d_size; + if (dwfl_module_relocations (m) > 0) + *eh_frame_hdr_addr = shdr->sh_addr - start + bias; + else + *eh_frame_hdr_addr = shdr->sh_addr; + eh_frame_hdr_seen = true; + } + if (eh_frame_seen && eh_frame_hdr_seen) + break; } // fetch .debug_frame info preferably from dwarf debuginfo file. @@ -4785,9 +4802,13 @@ dump_unwindsyms (Dwfl_Module *m, void *debug_frame = NULL; size_t debug_len = 0; void *eh_frame = NULL; + void *eh_frame_hdr = NULL; size_t eh_len = 0; + size_t eh_frame_hdr_len = 0; Dwarf_Addr eh_addr = 0; - get_unwind_data (m, &debug_frame, &eh_frame, &debug_len, &eh_len, &eh_addr); + Dwarf_Addr eh_frame_hdr_addr = 0; + get_unwind_data (m, &debug_frame, &eh_frame, &debug_len, &eh_len, &eh_addr, + &eh_frame_hdr, &eh_frame_hdr_len, &eh_frame_hdr_addr); if (debug_frame != NULL && debug_len > 0) { if (debug_len > MAX_UNWIND_TABLE_SIZE) @@ -4828,6 +4849,26 @@ dump_unwindsyms (Dwfl_Module *m, c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA */\n"; } + if (eh_frame_hdr != NULL && eh_frame_hdr_len > 0) + { + if (eh_frame_hdr_len > MAX_UNWIND_TABLE_SIZE) + throw semantic_error ("module eh header size too big"); + + c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n"; + c->output << "static uint8_t _stp_module_" << stpmod_idx + << "_eh_frame_hdr[] = \n"; + c->output << " {"; + for (size_t i = 0; i < eh_frame_hdr_len; i++) + { + int h = ((uint8_t *)eh_frame_hdr)[i]; + c->output << "0x" << hex << h << dec << ","; + if ((i + 1) % 16 == 0) + c->output << "\n" << " "; + } + c->output << "};\n"; + c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA */\n"; + } + if (debug_frame == NULL && eh_frame == NULL) { // There would be only a small benefit to warning. A user @@ -4883,8 +4924,9 @@ dump_unwindsyms (Dwfl_Module *m, c->output << "static struct _stp_module _stp_module_" << stpmod_idx << " = {\n"; c->output << ".name = " << lex_cast_qstring (modname) << ", \n"; c->output << ".path = " << lex_cast_qstring (mainfile) << ",\n"; - c->output << ".dwarf_module_base = 0x" << hex << base << dec << ", \n"; - c->output << ".eh_frame_addr = 0x" << hex << eh_addr << dec << ", \n"; + c->output << ".dwarf_module_base = 0x" << hex << base << ", \n"; + c->output << ".eh_frame_addr = 0x" << eh_addr << ", \n"; + c->output << ".unwind_hdr_addr = 0x" << eh_frame_hdr_addr << dec << ", \n"; if (debug_frame != NULL) { @@ -4907,18 +4949,26 @@ dump_unwindsyms (Dwfl_Module *m, c->output << ".eh_frame = " << "_stp_module_" << stpmod_idx << "_eh_frame, \n"; c->output << ".eh_frame_len = " << eh_len << ", \n"; + if (eh_frame_hdr) + { + c->output << ".unwind_hdr = " + << "_stp_module_" << stpmod_idx << "_eh_frame_hdr, \n"; + c->output << ".unwind_hdr_len = " << eh_frame_hdr_len << ", \n"; + } + else + { + c->output << ".unwind_hdr = NULL,\n"; + c->output << ".unwind_hdr_len = 0,\n"; + } c->output << "#else\n"; } c->output << ".eh_frame = NULL,\n"; c->output << ".eh_frame_len = 0,\n"; - - if (eh_frame != NULL) - c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA*/\n"; - c->output << ".unwind_hdr = NULL,\n"; c->output << ".unwind_hdr_len = 0,\n"; - + if (eh_frame != NULL) + c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA*/\n"; c->output << ".sections = _stp_module_" << stpmod_idx << "_sections" << ",\n"; c->output << ".num_sections = sizeof(_stp_module_" << stpmod_idx << "_sections)/" << "sizeof(struct _stp_section),\n"; -- 2.43.5