1//===------------------------- EHHeaderParser.hpp -------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is dual licensed under the MIT and the University of Illinois Open 6// Source Licenses. See LICENSE.TXT for details. 7// 8// 9// Parses ELF .eh_frame_hdr sections. 10// 11//===----------------------------------------------------------------------===// 12 13#ifndef __EHHEADERPARSER_HPP__ 14#define __EHHEADERPARSER_HPP__ 15 16#include "libunwind.h" 17 18#include "DwarfParser.hpp" 19 20namespace libunwind { 21 22/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section. 23/// 24/// See DWARF spec for details: 25/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html 26/// 27template <typename A> class EHHeaderParser { 28public: 29 typedef typename A::pint_t pint_t; 30 31 /// Information encoded in the EH frame header. 32 struct EHHeaderInfo { 33 pint_t eh_frame_ptr; 34 size_t fde_count; 35 pint_t table; 36 uint8_t table_enc; 37 }; 38 39 static void decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd, 40 EHHeaderInfo &ehHdrInfo); 41 static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, 42 uint32_t sectionLength, 43 typename CFI_Parser<A>::FDE_Info *fdeInfo, 44 typename CFI_Parser<A>::CIE_Info *cieInfo); 45 46private: 47 static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry, 48 pint_t ehHdrStart, pint_t ehHdrEnd, 49 uint8_t tableEnc, 50 typename CFI_Parser<A>::FDE_Info *fdeInfo, 51 typename CFI_Parser<A>::CIE_Info *cieInfo); 52 static size_t getTableEntrySize(uint8_t tableEnc); 53}; 54 55template <typename A> 56void EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart, 57 pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) { 58 pint_t p = ehHdrStart; 59 uint8_t version = addressSpace.get8(p++); 60 if (version != 1) 61 _LIBUNWIND_ABORT("Unsupported .eh_frame_hdr version"); 62 63 uint8_t eh_frame_ptr_enc = addressSpace.get8(p++); 64 uint8_t fde_count_enc = addressSpace.get8(p++); 65 ehHdrInfo.table_enc = addressSpace.get8(p++); 66 67 ehHdrInfo.eh_frame_ptr = 68 addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart); 69 ehHdrInfo.fde_count = 70 fde_count_enc == DW_EH_PE_omit 71 ? 0 72 : addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart); 73 ehHdrInfo.table = p; 74} 75 76template <typename A> 77bool EHHeaderParser<A>::decodeTableEntry( 78 A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd, 79 uint8_t tableEnc, typename CFI_Parser<A>::FDE_Info *fdeInfo, 80 typename CFI_Parser<A>::CIE_Info *cieInfo) { 81 // Have to decode the whole FDE for the PC range anyway, so just throw away 82 // the PC start. 83 addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart); 84 pint_t fde = 85 addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart); 86 const char *message = 87 CFI_Parser<A>::decodeFDE(addressSpace, fde, fdeInfo, cieInfo); 88 if (message != NULL) { 89 _LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s", 90 message); 91 return false; 92 } 93 94 return true; 95} 96 97template <typename A> 98bool EHHeaderParser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, 99 uint32_t sectionLength, 100 typename CFI_Parser<A>::FDE_Info *fdeInfo, 101 typename CFI_Parser<A>::CIE_Info *cieInfo) { 102 pint_t ehHdrEnd = ehHdrStart + sectionLength; 103 104 EHHeaderParser<A>::EHHeaderInfo hdrInfo; 105 EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd, hdrInfo); 106 107 size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc); 108 pint_t tableEntry; 109 110 size_t low = 0; 111 for (size_t len = hdrInfo.fde_count; len > 1;) { 112 size_t mid = low + (len / 2); 113 tableEntry = hdrInfo.table + mid * tableEntrySize; 114 pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd, 115 hdrInfo.table_enc, ehHdrStart); 116 117 if (start == pc) { 118 low = mid; 119 break; 120 } else if (start < pc) { 121 low = mid; 122 len -= (len / 2); 123 } else { 124 len /= 2; 125 } 126 } 127 128 tableEntry = hdrInfo.table + low * tableEntrySize; 129 if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd, 130 hdrInfo.table_enc, fdeInfo, cieInfo)) { 131 if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd) 132 return true; 133 } 134 135 return false; 136} 137 138template <typename A> 139size_t EHHeaderParser<A>::getTableEntrySize(uint8_t tableEnc) { 140 switch (tableEnc & 0x0f) { 141 case DW_EH_PE_sdata2: 142 case DW_EH_PE_udata2: 143 return 4; 144 case DW_EH_PE_sdata4: 145 case DW_EH_PE_udata4: 146 return 8; 147 case DW_EH_PE_sdata8: 148 case DW_EH_PE_udata8: 149 return 16; 150 case DW_EH_PE_sleb128: 151 case DW_EH_PE_uleb128: 152 _LIBUNWIND_ABORT("Can't binary search on variable length encoded data."); 153 case DW_EH_PE_omit: 154 return 0; 155 default: 156 _LIBUNWIND_ABORT("Unknown DWARF encoding for search table."); 157 } 158} 159 160} 161 162#endif 163