From 895a3deea4bc2202f6d4d50d0718567bec61cb8a Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Sun, 1 May 2005 15:14:01 +0000 Subject: [PATCH] Get DWARF CFI handling going on amd64 systems. This also required getting handling of augmentation strings right. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3584 --- coregrind/vg_dwarf.c | 314 +++++++++++++++++++++++++++++++++++++---- coregrind/vg_symtab2.c | 35 +++-- coregrind/vg_symtab2.h | 2 +- 3 files changed, 304 insertions(+), 47 deletions(-) diff --git a/coregrind/vg_dwarf.c b/coregrind/vg_dwarf.c index 2eb96e73c..a47eaf9a4 100644 --- a/coregrind/vg_dwarf.c +++ b/coregrind/vg_dwarf.c @@ -909,6 +909,15 @@ static void initCfiSI ( CfiSI* si ) 8 is the return address (EIP) */ +#if defined(__x86__) +#define FP_COL 5 +#define SP_COL 4 +#define RA_COL 8 +#elif defined(__amd64__) +#define FP_COL 6 +#define SP_COL 7 +#define RA_COL 16 +#endif /* the number of regs we are prepared to unwind */ #define N_CFI_REGS 20 @@ -944,6 +953,27 @@ enum dwarf_cfa_secondary_ops DW_CFA_hi_user = 0x3f }; +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + typedef struct { @@ -1018,18 +1048,18 @@ static void ppUnwindContext ( UnwindContext* ctx ) This is taken to be just after ctx's loc advances; hence the summary is up to but not including the current loc. */ -static Bool summarise_context_x86 ( /*OUT*/CfiSI* si, - Addr loc_start, - UnwindContext* ctx ) +static Bool summarise_context( /*OUT*/CfiSI* si, + Addr loc_start, + UnwindContext* ctx ) { initCfiSI(si); /* How to generate the CFA */ - if (ctx->cfa_reg == 4 /* ESP */) { + if (ctx->cfa_reg == SP_COL) { si->cfa_sprel = True; si->cfa_off = ctx->cfa_offset; } else - if (ctx->cfa_reg == 5 /* EBP */) { + if (ctx->cfa_reg == FP_COL) { si->cfa_sprel = False; si->cfa_off = ctx->cfa_offset; } else { @@ -1044,8 +1074,8 @@ static Bool summarise_context_x86 ( /*OUT*/CfiSI* si, default: goto failed; /* otherwise give up */ \ } - SUMMARISE_HOW(si->ra_how, si->ra_off, ctx->reg[8 /* Return address */] ); - SUMMARISE_HOW(si->fp_how, si->fp_off, ctx->reg[5 /* EBP */] ); + SUMMARISE_HOW(si->ra_how, si->ra_off, ctx->reg[RA_COL] ); + SUMMARISE_HOW(si->fp_how, si->fp_off, ctx->reg[FP_COL] ); # undef SUMMARISE_HOW @@ -1055,7 +1085,7 @@ static Bool summarise_context_x86 ( /*OUT*/CfiSI* si, si->sp_off = 0; /* also, gcc says "Undef" for %ebp when it is unchanged. So .. */ - if (ctx->reg[5 /* EBP */].tag == RR_Undef) + if (ctx->reg[FP_COL].tag == RR_Undef) si->fp_how = CFIR_SAME; /* knock out some obviously stupid cases */ @@ -1073,30 +1103,30 @@ static Bool summarise_context_x86 ( /*OUT*/CfiSI* si, return True; failed: - VG_(printf)("summarise_context_x86(loc_start = %p)" + VG_(printf)("summarise_context(loc_start = %p)" ": cannot summarise:\n ", loc_start); ppUnwindContext(ctx); return False; } -static void ppUnwindContext_x86_summary ( UnwindContext* ctx ) +static void ppUnwindContext_summary ( UnwindContext* ctx ) { VG_(printf)("0x%llx-1: ", (ULong)ctx->loc); - if (ctx->cfa_reg == 4 /* ESP */) { - VG_(printf)("SP/CFA=%d+%%esp ", ctx->cfa_offset); + if (ctx->cfa_reg == SP_COL) { + VG_(printf)("SP/CFA=%d+SP ", ctx->cfa_offset); } else - if (ctx->cfa_reg == 5 /* EBP */) { - VG_(printf)("SP/CFA=%d+%%ebp ", ctx->cfa_offset); + if (ctx->cfa_reg == FP_COL) { + VG_(printf)("SP/CFA=%d+FP ", ctx->cfa_offset); } else { VG_(printf)("SP/CFA=unknown ", ctx->cfa_offset); } VG_(printf)("RA="); - ppRegRule( &ctx->reg[8 /* Return address */] ); + ppRegRule( &ctx->reg[RA_COL] ); VG_(printf)("FP="); - ppRegRule( &ctx->reg[5 /* EBP */] ); + ppRegRule( &ctx->reg[FP_COL] ); VG_(printf)("\n"); } @@ -1108,6 +1138,41 @@ static inline Bool host_is_little_endian ( void ) } +static Short read_Short ( UChar* data ) +{ + vg_assert(host_is_little_endian()); + Short r = 0; + r = data[0] + | ( ((UInt)data[1]) << 8 ); + return r; +} + +static Int read_Int ( UChar* data ) +{ + vg_assert(host_is_little_endian()); + Int r = 0; + r = data[0] + | ( ((UInt)data[1]) << 8 ) + | ( ((UInt)data[2]) << 16 ) + | ( ((UInt)data[3]) << 24 ); + return r; +} + +static Long read_Long ( UChar* data ) +{ + vg_assert(host_is_little_endian()); + Long r = 0; + r = data[0] + | ( ((ULong)data[1]) << 8 ) + | ( ((ULong)data[2]) << 16 ) + | ( ((ULong)data[3]) << 24 ) + | ( ((ULong)data[4]) << 32 ) + | ( ((ULong)data[5]) << 40 ) + | ( ((ULong)data[6]) << 48 ) + | ( ((ULong)data[7]) << 56 ); + return r; +} + static UShort read_UShort ( UChar* data ) { vg_assert(host_is_little_endian()); @@ -1128,10 +1193,27 @@ static UInt read_UInt ( UChar* data ) return r; } +static ULong read_ULong ( UChar* data ) +{ + vg_assert(host_is_little_endian()); + ULong r = 0; + r = data[0] + | ( ((ULong)data[1]) << 8 ) + | ( ((ULong)data[2]) << 16 ) + | ( ((ULong)data[3]) << 24 ) + | ( ((ULong)data[4]) << 32 ) + | ( ((ULong)data[5]) << 40 ) + | ( ((ULong)data[6]) << 48 ) + | ( ((ULong)data[7]) << 56 ); + return r; +} + static Addr read_Addr ( UChar* data ) { if (sizeof(Addr) == 4) return read_UInt(data); + else if (sizeof(Addr) == 8) + return read_ULong(data); vg_assert(0); } @@ -1140,6 +1222,96 @@ static UChar read_UChar ( UChar* data ) return data[0]; } +static UChar default_address_encoding () +{ + switch (sizeof(Addr)) { + case 4: return DW_EH_PE_udata4; + case 8: return DW_EH_PE_udata8; + default: vg_assert(0); + } +} + +static UInt size_of_encoded_address ( UChar encoding ) +{ + if (encoding == DW_EH_PE_omit) + return 0; + + switch (encoding & 0x07) { + case DW_EH_PE_absptr: return sizeof(Addr); + case DW_EH_PE_udata2: return sizeof(UShort); + case DW_EH_PE_udata4: return sizeof(UInt); + case DW_EH_PE_udata8: return sizeof(ULong); + default: vg_assert(0); + } +} + +static Addr read_encoded_address ( UChar* data, UChar encoding, Int *nbytes, + UChar* ehframe, Addr ehframe_addr ) +{ + Addr base; + Int offset; + + vg_assert((encoding & DW_EH_PE_indirect) == 0); + + *nbytes = 0; + + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + base = 0; + break; + case DW_EH_PE_pcrel: + base = ehframe_addr + ( data - ehframe ); + break; + case DW_EH_PE_datarel: + vg_assert(0); + base = /* data base address */ 0; + break; + case DW_EH_PE_textrel: + vg_assert(0); + base = /* text base address */ 0; + break; + case DW_EH_PE_funcrel: + base = 0; + break; + case DW_EH_PE_aligned: + base = 0; + offset = data - ehframe; + if ((offset % sizeof(Addr)) != 0) { + *nbytes = sizeof(Addr) - (offset % sizeof(Addr)); + data += *nbytes; + } + break; + default: + vg_assert(0); + } + + if ((encoding & 0x0f) == 0x00) + encoding |= default_address_encoding(); + + switch (encoding & 0x0f) { + case DW_EH_PE_udata2: + *nbytes += sizeof(UShort); + return base + read_UShort(data); + case DW_EH_PE_udata4: + *nbytes += sizeof(UInt); + return base + read_UInt(data); + case DW_EH_PE_udata8: + *nbytes += sizeof(ULong); + return base + read_ULong(data); + case DW_EH_PE_sdata2: + *nbytes += sizeof(Short); + return base + read_Short(data); + case DW_EH_PE_sdata4: + *nbytes += sizeof(Int); + return base + read_Int(data); + case DW_EH_PE_sdata8: + *nbytes += sizeof(Long); + return base + read_Long(data); + default: + vg_assert(0); + } +} + /* Run a CFI instruction, and also return its length. Returns 0 if the instruction could not be executed. @@ -1183,10 +1355,20 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx, switch (lo6) { case DW_CFA_nop: break; + case DW_CFA_set_loc: + ctx->loc = read_Addr(&instr[i]) - ctx->initloc; i+= sizeof(Addr); + break; case DW_CFA_advance_loc1: - vg_assert(0); delta = (UInt)read_UChar(&instr[i]); i+= sizeof(UChar); - VG_(printf)("DW_CFA_advance_loc1(%d)\n", delta); + ctx->loc += delta; + break; + case DW_CFA_advance_loc2: + delta = (UInt)read_UShort(&instr[i]); i+= sizeof(UShort); + ctx->loc += delta; + break; + case DW_CFA_advance_loc4: + delta = (UInt)read_UInt(&instr[i]); i+= sizeof(UInt); + ctx->loc += delta; break; case DW_CFA_def_cfa: @@ -1222,8 +1404,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx, break; } default: - vg_assert(0); - VG_(printf)("0:%d\n", (Int)lo6); + VG_(printf)("Unhandled CFI instruction 0:%d\n", (Int)lo6); + i = 0; break; } @@ -1236,11 +1418,17 @@ static Int show_CF_instruction ( UChar* instr ) { UInt delta; Int off, reg, nleb; + Addr loc; Int i = 0; UChar hi2 = (instr[i] >> 6) & 3; UChar lo6 = instr[i] & 0x3F; i++; + if (0) VG_(printf)("raw:%x/%x:%x:%x:%x:%x:%x:%x:%x:%x\n", + hi2, lo6, + instr[i+0], instr[i+1], instr[i+2], instr[i+3], + instr[i+4], instr[i+5], instr[i+6], instr[i+7] ); + if (hi2 == DW_CFA_advance_loc) { VG_(printf)("DW_CFA_advance_loc(%d)\n", (Int)lo6); return i; @@ -1266,11 +1454,26 @@ static Int show_CF_instruction ( UChar* instr ) VG_(printf)("DW_CFA_nop\n"); break; + case DW_CFA_set_loc: + loc = read_Addr(&instr[i]); i+= sizeof(Addr); + VG_(printf)("DW_CFA_set_loc(%p)\n", loc); + break; + case DW_CFA_advance_loc1: delta = (UInt)read_UChar(&instr[i]); i+= sizeof(UChar); VG_(printf)("DW_CFA_advance_loc1(%d)\n", delta); break; + case DW_CFA_advance_loc2: + delta = (UInt)read_UShort(&instr[i]); i+= sizeof(UShort); + VG_(printf)("DW_CFA_advance_loc2(%d)\n", delta); + break; + + case DW_CFA_advance_loc4: + delta = (UInt)read_UInt(&instr[i]); i+= sizeof(UInt); + VG_(printf)("DW_CFA_advance_loc4(%d)\n", delta); + break; + case DW_CFA_def_cfa: reg = read_leb128( &instr[i], &nleb, 0 ); i += nleb; @@ -1328,7 +1531,7 @@ Bool run_CF_instructions ( SegInfo* si, Int j, i = 0; Addr loc_prev; if (0) ppUnwindContext(ctx); - if (0) ppUnwindContext_x86_summary(ctx); + if (0) ppUnwindContext_summary(ctx); while (True) { loc_prev = ctx->loc; if (i >= ilen) break; @@ -1339,7 +1542,7 @@ Bool run_CF_instructions ( SegInfo* si, i += j; if (0) ppUnwindContext(ctx); if (loc_prev != ctx->loc && si) { - summarise_context_x86 ( &cfisi, loc_prev, ctx ); + summarise_context ( &cfisi, loc_prev, ctx ); VG_(addCfiSI)(si, &cfisi); } } @@ -1347,7 +1550,7 @@ Bool run_CF_instructions ( SegInfo* si, loc_prev = ctx->loc; ctx->loc = fde_arange; if (si) { - summarise_context_x86 ( &cfisi, loc_prev, ctx ); + summarise_context ( &cfisi, loc_prev, ctx ); VG_(addCfiSI)(si, &cfisi); } } @@ -1356,7 +1559,7 @@ Bool run_CF_instructions ( SegInfo* si, void VG_(read_callframe_info_dwarf2) - ( /*OUT*/SegInfo* si, UChar* ehframe, Int ehframe_sz ) + ( /*OUT*/SegInfo* si, UChar* ehframe, Int ehframe_sz, Addr ehframe_addr ) { UnwindContext ctx; Int nbytes; @@ -1374,6 +1577,8 @@ void VG_(read_callframe_info_dwarf2) UChar* cie_instrs = NULL; Int cie_ilen = 0; + Bool saw_z_augmentation = False; + UChar address_encoding = default_address_encoding(); /* Loop over CIEs/FDEs */ @@ -1425,9 +1630,9 @@ void VG_(read_callframe_info_dwarf2) data += 1 + VG_(strlen)(cie_augmentation); if (0) VG_(printf)("cie.augment = \"%s\"\n", cie_augmentation); - if (0 != VG_(strcmp)(cie_augmentation, "")) { - how = "non-NULL cie.augmentation"; - goto bad; + if (cie_augmentation[0] == 'e' && cie_augmentation[1] == 'h') { + data += sizeof(Addr); + cie_augmentation += 2; } cie_codeaf = read_leb128( data, &nbytes, 0); @@ -1441,9 +1646,47 @@ void VG_(read_callframe_info_dwarf2) UChar cie_rareg = read_UChar(data); data += sizeof(UChar); if (0) VG_(printf)("cie.ra_reg = %d\n", (Int)cie_rareg); + saw_z_augmentation = *cie_augmentation == 'z'; + if (saw_z_augmentation) { + UInt length = read_leb128( data, &nbytes, 0); + data += nbytes; + cie_instrs = data + length; + cie_augmentation++; + } else { + cie_instrs = NULL; + } + + while (*cie_augmentation) { + switch (*cie_augmentation) { + case 'L': + data++; + cie_augmentation++; + break; + case 'R': + address_encoding = read_UChar(data); data += sizeof(UChar); + cie_augmentation++; + break; + case 'P': + data += size_of_encoded_address(address_encoding); + cie_augmentation++; + break; + default: + if (cie_instrs == NULL) { + how = "unhandled cie.augmentation"; + goto bad; + } + data = cie_instrs; + goto done_augmentation; + } + } + + done_augmentation: + + if (0) VG_(printf)("cie.encoding = 0x%x\n", address_encoding); + cie_instrs = data; cie_ilen = ciefde_start + ciefde_len + sizeof(UInt) - data; - if (0) VG_(printf)("cie.instrs = %p\n", (Int)cie_instrs); + if (0) VG_(printf)("cie.instrs = %p\n", cie_instrs); if (0) VG_(printf)("cie.ilen = %d\n", (Int)cie_ilen); if (cie_ilen < 0 || cie_ilen > ehframe_sz) { @@ -1470,15 +1713,24 @@ void VG_(read_callframe_info_dwarf2) goto bad; } - Addr fde_initloc = read_Addr(data); data += sizeof(Addr); + Addr fde_initloc = read_encoded_address(data, address_encoding, + &nbytes, ehframe, ehframe_addr); + data += nbytes; if (0) VG_(printf)("fde.initloc = %p\n", (void*)fde_initloc); - UWord fde_arange = read_Addr(data); data += sizeof(Addr); + UWord fde_arange = read_encoded_address(data, address_encoding & 0xf, + &nbytes, ehframe, ehframe_addr); + data += nbytes; if (0) VG_(printf)("fde.arangec = %p\n", (void*)fde_arange); + if (saw_z_augmentation) { + data += read_leb128( data, &nbytes, 0); + data += nbytes; + } + UChar* fde_instrs = data; Int fde_ilen = ciefde_start + ciefde_len + sizeof(UInt) - data; - if (0) VG_(printf)("fde.instrs = %p\n", (Int)fde_instrs); + if (0) VG_(printf)("fde.instrs = %p\n", fde_instrs); if (0) VG_(printf)("fde.ilen = %d\n", (Int)fde_ilen); if (fde_ilen < 0 || fde_ilen > ehframe_sz) { diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c index 3270c8091..88613e41a 100644 --- a/coregrind/vg_symtab2.c +++ b/coregrind/vg_symtab2.c @@ -1505,11 +1505,15 @@ Bool read_lib_symbols ( SegInfo* si ) UInt dwarf1l_sz = 0; UInt ehframe_sz = 0; + /* Section virtual addresses */ + Addr dummy_addr = 0; + Addr ehframe_addr = 0; + Bool has_debuginfo = False; /* Find all interesting sections */ for (i = 0; i < ehdr->e_shnum; i++) { -# define FIND(sec_name, sec_data, sec_size, in_exec, type) \ +# define FIND(sec_name, sec_data, sec_size, sec_addr, in_exec, type) \ if (0 == VG_(strcmp)(sec_name, sh_strtab + shdr[i].sh_name)) { \ if (0 != sec_data) \ VG_(core_panic)("repeated section!\n"); \ @@ -1518,6 +1522,7 @@ Bool read_lib_symbols ( SegInfo* si ) else \ sec_data = (type)(oimage + shdr[i].sh_offset); \ sec_size = shdr[i].sh_size; \ + sec_addr = si->offset + shdr[i].sh_addr; \ TRACE_SYMTAB( "%18s: %p .. %p\n", \ sec_name, sec_data, sec_data + sec_size - 1); \ if ( shdr[i].sh_offset + sec_size > n_oimage ) { \ @@ -1528,22 +1533,22 @@ Bool read_lib_symbols ( SegInfo* si ) /* Nb: must find where .got and .plt sections will be in the * executable image, not in the object image transiently loaded. */ - FIND(".dynsym", o_dynsym, o_dynsym_sz, 0, ElfXX_Sym*) - else FIND(".dynstr", o_dynstr, o_dynstr_sz, 0, UChar*) - else FIND(".symtab", o_symtab, o_symtab_sz, 0, ElfXX_Sym*) - else FIND(".strtab", o_strtab, o_strtab_sz, 0, UChar*) + FIND(".dynsym", o_dynsym, o_dynsym_sz, dummy_addr, 0, ElfXX_Sym*) + else FIND(".dynstr", o_dynstr, o_dynstr_sz, dummy_addr, 0, UChar*) + else FIND(".symtab", o_symtab, o_symtab_sz, dummy_addr, 0, ElfXX_Sym*) + else FIND(".strtab", o_strtab, o_strtab_sz, dummy_addr, 0, UChar*) - else FIND(".gnu_debuglink", debuglink, debuglink_sz, 0, Char*) + else FIND(".gnu_debuglink", debuglink, debuglink_sz, dummy_addr, 0, Char*) - else FIND(".stab", stab, stab_sz, 0, UChar*) - else FIND(".stabstr", stabstr, stabstr_sz, 0, UChar*) - else FIND(".debug_line", debug_line, debug_line_sz, 0, UChar*) - else FIND(".debug", dwarf1d, dwarf1d_sz, 0, UChar*) - else FIND(".line", dwarf1l, dwarf1l_sz, 0, UChar*) - else FIND(".eh_frame", ehframe, ehframe_sz, 0, UChar*) + else FIND(".stab", stab, stab_sz, dummy_addr, 0, UChar*) + else FIND(".stabstr", stabstr, stabstr_sz, dummy_addr, 0, UChar*) + else FIND(".debug_line", debug_line, debug_line_sz, dummy_addr, 0, UChar*) + else FIND(".debug", dwarf1d, dwarf1d_sz, dummy_addr, 0, UChar*) + else FIND(".line", dwarf1l, dwarf1l_sz, dummy_addr, 0, UChar*) + else FIND(".eh_frame", ehframe, ehframe_sz, ehframe_addr, 0, UChar*) - else FIND(".got", si->got_start, si->got_size, 1, Addr) - else FIND(".plt", si->plt_start, si->plt_size, 1, Addr) + else FIND(".got", si->got_start, si->got_size, dummy_addr, 1, Addr) + else FIND(".plt", si->plt_start, si->plt_size, dummy_addr, 1, Addr) # undef FIND @@ -1615,7 +1620,7 @@ Bool read_lib_symbols ( SegInfo* si ) /* Read .eh_frame (call-frame-info) if any */ if (ehframe && ehframe_sz > 4) { - VG_(read_callframe_info_dwarf2) ( si, ehframe, ehframe_sz ); + VG_(read_callframe_info_dwarf2) ( si, ehframe, ehframe_sz, ehframe_addr ); } /* Read the stabs and/or dwarf2 debug information, if any. */ diff --git a/coregrind/vg_symtab2.h b/coregrind/vg_symtab2.h index 81bac30ce..f1f141c0b 100644 --- a/coregrind/vg_symtab2.h +++ b/coregrind/vg_symtab2.h @@ -255,7 +255,7 @@ void VG_(read_debuginfo_dwarf1) ( SegInfo* si, CFI reader -------------------- */ void VG_(read_callframe_info_dwarf2) - ( /*OUT*/SegInfo* si, UChar* ehframe, Int ehframe_sz ); + ( /*OUT*/SegInfo* si, UChar* ehframe, Int ehframe_sz, Addr ehframe_addr ); #endif /* _VG_SYMTYPE_H */