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