1 //===----------------------------------------------------------------------===//
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
8 // Parses ELF .eh_frame_hdr sections.
10 //===----------------------------------------------------------------------===//
12 #ifndef __EHHEADERPARSER_HPP__
13 #define __EHHEADERPARSER_HPP__
15 #include "libunwind.h"
17 #include "DwarfParser.hpp"
21 /// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section.
23 /// See DWARF spec for details:
24 /// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
26 template <typename A
> class EHHeaderParser
{
28 typedef typename
A::pint_t pint_t
;
30 /// Information encoded in the EH frame header.
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
);
46 static bool decodeTableEntry(A
&addressSpace
, pint_t
&tableEntry
,
47 pint_t ehHdrStart
, pint_t ehHdrEnd
,
49 typename CFI_Parser
<A
>::FDE_Info
*fdeInfo
,
50 typename CFI_Parser
<A
>::CIE_Info
*cieInfo
);
51 static size_t getTableEntrySize(uint8_t tableEnc
);
55 bool EHHeaderParser
<A
>::decodeEHHdr(A
&addressSpace
, pint_t ehHdrStart
,
56 pint_t ehHdrEnd
, EHHeaderInfo
&ehHdrInfo
) {
57 pint_t p
= ehHdrStart
;
59 // Ensure that we don't read data beyond the end of .eh_frame_hdr
60 if (ehHdrEnd
- ehHdrStart
< 4) {
61 // Don't print a message for an empty .eh_frame_hdr (this can happen if
62 // the linker script defines symbols for it even in the empty case).
63 if (ehHdrEnd
== ehHdrStart
)
65 _LIBUNWIND_LOG("unsupported .eh_frame_hdr at %" PRIx64
66 ": need at least 4 bytes of data but only got %zd",
67 static_cast<uint64_t>(ehHdrStart
),
68 static_cast<size_t>(ehHdrEnd
- ehHdrStart
));
71 uint8_t version
= addressSpace
.get8(p
++);
73 _LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8
" at %" PRIx64
,
74 version
, static_cast<uint64_t>(ehHdrStart
));
78 uint8_t eh_frame_ptr_enc
= addressSpace
.get8(p
++);
79 uint8_t fde_count_enc
= addressSpace
.get8(p
++);
80 ehHdrInfo
.table_enc
= addressSpace
.get8(p
++);
82 ehHdrInfo
.eh_frame_ptr
=
83 addressSpace
.getEncodedP(p
, ehHdrEnd
, eh_frame_ptr_enc
, ehHdrStart
);
85 fde_count_enc
== DW_EH_PE_omit
87 : addressSpace
.getEncodedP(p
, ehHdrEnd
, fde_count_enc
, ehHdrStart
);
94 bool EHHeaderParser
<A
>::decodeTableEntry(
95 A
&addressSpace
, pint_t
&tableEntry
, pint_t ehHdrStart
, pint_t ehHdrEnd
,
96 uint8_t tableEnc
, typename CFI_Parser
<A
>::FDE_Info
*fdeInfo
,
97 typename CFI_Parser
<A
>::CIE_Info
*cieInfo
) {
98 // Have to decode the whole FDE for the PC range anyway, so just throw away
100 addressSpace
.getEncodedP(tableEntry
, ehHdrEnd
, tableEnc
, ehHdrStart
);
102 addressSpace
.getEncodedP(tableEntry
, ehHdrEnd
, tableEnc
, ehHdrStart
);
103 const char *message
=
104 CFI_Parser
<A
>::decodeFDE(addressSpace
, fde
, fdeInfo
, cieInfo
);
105 if (message
!= NULL
) {
106 _LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s",
114 template <typename A
>
115 bool EHHeaderParser
<A
>::findFDE(A
&addressSpace
, pint_t pc
, pint_t ehHdrStart
,
116 uint32_t sectionLength
,
117 typename CFI_Parser
<A
>::FDE_Info
*fdeInfo
,
118 typename CFI_Parser
<A
>::CIE_Info
*cieInfo
) {
119 pint_t ehHdrEnd
= ehHdrStart
+ sectionLength
;
121 EHHeaderParser
<A
>::EHHeaderInfo hdrInfo
;
122 if (!EHHeaderParser
<A
>::decodeEHHdr(addressSpace
, ehHdrStart
, ehHdrEnd
,
126 if (hdrInfo
.fde_count
== 0) return false;
128 size_t tableEntrySize
= getTableEntrySize(hdrInfo
.table_enc
);
132 for (size_t len
= hdrInfo
.fde_count
; len
> 1;) {
133 size_t mid
= low
+ (len
/ 2);
134 tableEntry
= hdrInfo
.table
+ mid
* tableEntrySize
;
135 pint_t start
= addressSpace
.getEncodedP(tableEntry
, ehHdrEnd
,
136 hdrInfo
.table_enc
, ehHdrStart
);
141 } else if (start
< pc
) {
149 tableEntry
= hdrInfo
.table
+ low
* tableEntrySize
;
150 if (decodeTableEntry(addressSpace
, tableEntry
, ehHdrStart
, ehHdrEnd
,
151 hdrInfo
.table_enc
, fdeInfo
, cieInfo
)) {
152 if (pc
>= fdeInfo
->pcStart
&& pc
< fdeInfo
->pcEnd
)
159 template <typename A
>
160 size_t EHHeaderParser
<A
>::getTableEntrySize(uint8_t tableEnc
) {
161 switch (tableEnc
& 0x0f) {
162 case DW_EH_PE_sdata2
:
163 case DW_EH_PE_udata2
:
165 case DW_EH_PE_sdata4
:
166 case DW_EH_PE_udata4
:
168 case DW_EH_PE_sdata8
:
169 case DW_EH_PE_udata8
:
171 case DW_EH_PE_sleb128
:
172 case DW_EH_PE_uleb128
:
173 _LIBUNWIND_ABORT("Can't binary search on variable length encoded data.");
177 _LIBUNWIND_ABORT("Unknown DWARF encoding for search table.");