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