From: <sv...@va...> - 2005-06-09 19:28:53
|
Author: sewardj Date: 2005-06-09 20:27:25 +0100 (Thu, 09 Jun 2005) New Revision: 3861 Log: More CFI-reader hacking. Thus far the reader has assumed that each FDE refers to the immediately preceding CIE, and gives up if that isn't so. Well, that isn't so, and this commit fixes it. Now FDEs may refer to CIEs seen arbitrarily far back. This fixes some missing stack traces on AMD64. Also add some comments giving a top-level sketch of how the CFI reader works. Modified: trunk/coregrind/m_debuginfo/dwarf.c Modified: trunk/coregrind/m_debuginfo/dwarf.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/coregrind/m_debuginfo/dwarf.c 2005-06-09 14:17:47 UTC (rev 3860= ) +++ trunk/coregrind/m_debuginfo/dwarf.c 2005-06-09 19:27:25 UTC (rev 3861= ) @@ -947,8 +947,10 @@ =20 =20 /* RegRule and UnwindContext are used temporarily to do the unwinding. - The result is then summarised into a CfiSI, if possible. */ - + The result is then summarised into a sequence of CfiSIs, if + possible. UnwindContext effectively holds the state of the + abstract machine whilst it is running. +*/ typedef struct { enum { RR_Undef, RR_Same, RR_CFAoff, RR_Reg, RR_Arch, RR_Expr } ta= g; @@ -1719,26 +1721,50 @@ =20 /* ------------ Main entry point for CFI reading ------------ */ =20 +typedef + struct { + /* This gives the CIE an identity to which FDEs will refer. */ + UInt offset; + /* Code, data factors. */ + Int code_a_f; + Int data_a_f; + /* Return-address pseudo-register. */ + Int ra_reg; + UChar address_encoding; + /* Where are the instrs? Note, this are simply pointers back to + the transiently-mapped-in section. */ + UChar* instrs; + Int ilen; + /* God knows .. don't ask */ + Bool saw_z_augmentation; + } + CIE; + +static void init_CIE ( CIE* cie ) +{ + cie->offset =3D 0; + cie->code_a_f =3D 0; + cie->data_a_f =3D 0; + cie->ra_reg =3D 0; + cie->address_encoding =3D 0; + cie->instrs =3D NULL; + cie->ilen =3D 0; + cie->saw_z_augmentation =3D False; +} + +#define N_CIEs 200 +static CIE the_CIEs[N_CIEs]; + + void VG_(read_callframe_info_dwarf2)=20 ( /*OUT*/SegInfo* si,=20 UChar* ehframe, Int ehframe_sz, Addr ehframe_addr ) { - UnwindContext ctx, restore_ctx; - Int nbytes; + Int nbytes; HChar* how =3D NULL; - Int cie_codeaf =3D 0; - Int cie_dataaf =3D 0; - Int cie_rareg =3D 0; - Bool ok; - - UChar* current_cie =3D NULL; + Int n_CIEs =3D 0; UChar* data =3D ehframe; =20 - UChar* cie_instrs =3D NULL; - Int cie_ilen =3D 0; - Bool saw_z_augmentation =3D False; - UChar address_encoding =3D default_Addr_encoding(); - if (VG_(clo_trace_cfi)) { VG_(printf)("\n-----------------------------------------------\n")= ; VG_(printf)("CFI info: ehframe %p, ehframe_sz %d\n", @@ -1749,6 +1775,25 @@ =20 /* Loop over CIEs/FDEs */ =20 + /* Conceptually, the frame info is a sequence of FDEs, one for each + function. Inside an FDE is a miniature program for a special + state machine, which, when run, produces the stack-unwinding + info for that function. + + Because the FDEs typically have much in common, and because the + DWARF designers appear to have been fanatical about space + saving, the common parts are factored out into so-called CIEs. + That means that what we traverse is a sequence of structs, each + of which is either a FDE (usually) or a CIE (occasionally). + Each FDE has a field indicating which CIE is the one pertaining + to it. + + The following loop traverses the sequence. FDEs are dealt with + immediately; once we harvest the useful info in an FDE, it is + then forgotten about. By contrast, CIEs are validated and + dumped into an array, because later FDEs may refer to any + previously-seen CIE. + */ while (True) { =20 /* Are we done? */ @@ -1774,8 +1819,8 @@ VG_(printf)("cie/fde.length =3D %d\n", ciefde_len); =20 /* Apparently, if the .length field is zero, we are at the end - of the sequence. ?? Neither the DWARF2 spec not the AMD64 - ABI spec say this, though. */ + of the sequence. ?? Neither the DWARF2 spec not the AMD64 + ABI spec say this, though. */ if (ciefde_len =3D=3D 0) { if (data =3D=3D ehframe + ehframe_sz) return; @@ -1783,20 +1828,36 @@ goto bad; } =20 - UInt cie_pointer =3D read_UInt(data); data +=3D sizeof(UInt); + UInt cie_pointer =3D read_UInt(data);=20 + data +=3D sizeof(UInt); /* XXX see XXX below */ if (VG_(clo_trace_cfi))=20 VG_(printf)("cie.pointer =3D %d\n", cie_pointer); =20 /* If cie_pointer is zero, we've got a CIE; else it's an FDE. */ if (cie_pointer =3D=3D 0) { =20 - /* Remember the start proper of the current CIE */ - current_cie =3D ciefde_start + sizeof(UInt); + Int this_CIE; =20 /* --------- CIE --------- */ - if (VG_(clo_trace_cfi))=20 - VG_(printf)("------ new CIE ------\n"); + if (1||VG_(clo_trace_cfi))=20 + VG_(printf)("------ new CIE (#%d of 0 .. %d) ------\n",=20 + n_CIEs, N_CIEs - 1); =20 + /* Allocate a new CIE record. */ + vg_assert(n_CIEs >=3D 0 && n_CIEs <=3D N_CIEs); + if (n_CIEs =3D=3D N_CIEs) { + how =3D "N_CIEs is too low. Increase and recompile."; + goto bad; + } + + this_CIE =3D n_CIEs; + n_CIEs++; + init_CIE( &the_CIEs[this_CIE] ); + + /* Record its offset. This is how we will find it again + later when looking at an FDE. */ + the_CIEs[this_CIE].offset =3D ciefde_start - ehframe; + UChar cie_version =3D read_UChar(data); data +=3D sizeof(UChar)= ; if (VG_(clo_trace_cfi)) VG_(printf)("cie.version =3D %d\n", (Int)cie_version); @@ -1815,35 +1876,41 @@ cie_augmentation +=3D 2; } =20 - cie_codeaf =3D read_leb128( data, &nbytes, 0); + the_CIEs[this_CIE].code_a_f =3D read_leb128( data, &nbytes, 0); data +=3D nbytes; if (VG_(clo_trace_cfi))=20 - VG_(printf)("cie.code_af =3D %d\n", cie_codeaf); + VG_(printf)("cie.code_af =3D %d\n",=20 + the_CIEs[this_CIE].code_a_f); =20 - cie_dataaf =3D read_leb128( data, &nbytes, 1); + the_CIEs[this_CIE].data_a_f =3D read_leb128( data, &nbytes, 1); data +=3D nbytes; if (VG_(clo_trace_cfi))=20 - VG_(printf)("cie.data_af =3D %d\n", cie_dataaf); + VG_(printf)("cie.data_af =3D %d\n", + the_CIEs[this_CIE].data_a_f); =20 - cie_rareg =3D read_UChar(data); data +=3D sizeof(UChar); + the_CIEs[this_CIE].ra_reg =3D (Int)read_UChar(data);=20 + data +=3D sizeof(UChar); if (VG_(clo_trace_cfi))=20 - VG_(printf)("cie.ra_reg =3D %d\n", (Int)cie_rareg); - if (cie_rareg < 0 || cie_rareg >=3D N_CFI_REGS) { + VG_(printf)("cie.ra_reg =3D %d\n",=20 + the_CIEs[this_CIE].ra_reg); + if (the_CIEs[this_CIE].ra_reg < 0=20 + || the_CIEs[this_CIE].ra_reg >=3D N_CFI_REGS) { how =3D "cie.ra_reg has implausible value"; goto bad; } =20 - saw_z_augmentation =3D *cie_augmentation =3D=3D 'z'; - if (saw_z_augmentation) { + the_CIEs[this_CIE].saw_z_augmentation=20 + =3D *cie_augmentation =3D=3D 'z'; + if (the_CIEs[this_CIE].saw_z_augmentation) { UInt length =3D read_leb128( data, &nbytes, 0); data +=3D nbytes; - cie_instrs =3D data + length; + the_CIEs[this_CIE].instrs =3D data + length; cie_augmentation++; } else { - cie_instrs =3D NULL; + the_CIEs[this_CIE].instrs =3D NULL; } =20 - address_encoding =3D default_Addr_encoding(); + the_CIEs[this_CIE].address_encoding =3D default_Addr_encoding()= ; =20 while (*cie_augmentation) { switch (*cie_augmentation) { @@ -1852,7 +1919,8 @@ cie_augmentation++; break; case 'R': - address_encoding =3D read_UChar(data); data +=3D sizeo= f(UChar); + the_CIEs[this_CIE].address_encoding=20 + =3D read_UChar(data); data +=3D sizeof(UChar); cie_augmentation++; break; case 'P': @@ -1861,11 +1929,11 @@ cie_augmentation++; break; default: - if (cie_instrs =3D=3D NULL) { + if (the_CIEs[this_CIE].instrs =3D=3D NULL) { how =3D "unhandled cie.augmentation"; goto bad; } - data =3D cie_instrs; + data =3D the_CIEs[this_CIE].instrs; goto done_augmentation; } } @@ -1873,55 +1941,72 @@ done_augmentation: =20 if (VG_(clo_trace_cfi))=20 - VG_(printf)("cie.encoding =3D 0x%x\n", address_encoding); + VG_(printf)("cie.encoding =3D 0x%x\n",=20 + the_CIEs[this_CIE].address_encoding); =20 - cie_instrs =3D data; - cie_ilen =3D ciefde_start + ciefde_len + sizeof(UInt) - data; + the_CIEs[this_CIE].instrs =3D data; + the_CIEs[this_CIE].ilen + =3D ciefde_start + ciefde_len + sizeof(UInt) - data; if (VG_(clo_trace_cfi)) { - VG_(printf)("cie.instrs =3D %p\n", cie_instrs); - VG_(printf)("cie.ilen =3D %d\n", (Int)cie_ilen); + VG_(printf)("cie.instrs =3D %p\n", the_CIEs[this_CIE].i= nstrs); + VG_(printf)("cie.ilen =3D %d\n", the_CIEs[this_CIE].i= len); } =20 - if (cie_ilen < 0 || cie_ilen > ehframe_sz) { + if (the_CIEs[this_CIE].ilen < 0 + || the_CIEs[this_CIE].ilen > ehframe_sz) { how =3D "implausible # cie initial insns"; goto bad; } =20 - data +=3D cie_ilen; + data +=3D the_CIEs[this_CIE].ilen; =20 if (VG_(clo_trace_cfi))=20 - show_CF_instructions(cie_instrs, cie_ilen); + show_CF_instructions(the_CIEs[this_CIE].instrs,=20 + the_CIEs[this_CIE].ilen); =20 } else { =20 + UnwindContext ctx, restore_ctx; + Int cie; + UInt look_for; + Bool ok; + /* --------- FDE --------- */ =20 - /* Ensure that (1) we have a valid CIE, and (2) that it is - indeed the CIE referred to by this FDE. */ - if (current_cie =3D=3D NULL) { - how =3D "FDE with no preceding CIE"; + /* Find the relevant CIE. The CIE we want is located + cie_pointer bytes back from here. */ + + /* re sizeof(UInt), matches XXX above. For 64-bit dwarf this + will have to be a ULong instead. */ + look_for =3D (data - sizeof(UInt) - ehframe) - cie_pointer; + + for (cie =3D 0; cie < n_CIEs; cie++) { + if (0) VG_(printf)("look for %d %d\n", + look_for, the_CIEs[cie].offset ); + if (the_CIEs[cie].offset =3D=3D look_for) + break; + } + vg_assert(cie >=3D 0 && cie <=3D n_CIEs); + if (cie =3D=3D n_CIEs) { + how =3D "FDE refers to not-findable CIE"; goto bad; - } - if (cie_pointer !=3D data - current_cie) { - how =3D "FDE does not refer to preceding CIE"; - goto bad; - } + } =20 Addr fde_initloc=20 - =3D read_encoded_Addr(data, address_encoding, + =3D read_encoded_Addr(data, the_CIEs[cie].address_encoding, &nbytes, ehframe, ehframe_addr); data +=3D nbytes; if (VG_(clo_trace_cfi))=20 VG_(printf)("fde.initloc =3D %p\n", (void*)fde_initloc); =20 UWord fde_arange=20 - =3D read_encoded_Addr(data, address_encoding & 0xf, + =3D read_encoded_Addr(data, the_CIEs[cie].address_encoding &= 0xf, &nbytes, ehframe, ehframe_addr); data +=3D nbytes; if (VG_(clo_trace_cfi))=20 VG_(printf)("fde.arangec =3D %p\n", (void*)fde_arange); =20 - if (saw_z_augmentation) { + if (the_CIEs[cie].saw_z_augmentation) { data +=3D read_leb128( data, &nbytes, 0); data +=3D nbytes; } @@ -1944,15 +2029,16 @@ show_CF_instructions(fde_instrs, fde_ilen); =20 initUnwindContext(&ctx); - ctx.code_a_f =3D cie_codeaf; - ctx.data_a_f =3D cie_dataaf; + ctx.code_a_f =3D the_CIEs[cie].code_a_f; + ctx.data_a_f =3D the_CIEs[cie].data_a_f; ctx.initloc =3D fde_initloc; - ctx.ra_reg =3D cie_rareg; + ctx.ra_reg =3D the_CIEs[cie].ra_reg; =20 initUnwindContext(&restore_ctx); =20 ok =3D run_CF_instructions( - NULL, &ctx, cie_instrs, cie_ilen, 0, NULL); + NULL, &ctx, the_CIEs[cie].instrs,=20 + the_CIEs[cie].ilen, 0, NULL); if (ok) { restore_ctx =3D ctx; ok =3D run_CF_instructions( |