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
This commit is contained in:
Tom Hughes
2005-05-01 15:14:01 +00:00
parent d86f35b2dc
commit 895a3deea4
3 changed files with 304 additions and 47 deletions

View File

@@ -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) {

View File

@@ -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. */

View File

@@ -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 */