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