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