2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/eh-frame.h"
19 #include "hphp/util/assertions.h"
20 #include "hphp/util/trace.h"
22 #include <folly/ScopeGuard.h>
30 void __register_frame(const void*) {}
31 void __deregister_frame(const void*) {}
34 * libgcc exports these for registering EH information for dynamically-loaded
35 * objects. The argument is a pointer to data in the format you find in an
38 extern "C" void __register_frame(const void*);
39 extern "C" void __deregister_frame(const void*);
44 TRACE_SET_MOD(ehframe
);
46 ///////////////////////////////////////////////////////////////////////////////
48 using exprlen_t
= uint8_t;
50 void EHFrameWriter::dw_cfa(uint8_t op
) {
51 // Only allow CFA opcodes when we're writing an entry.
52 assertx(m_cie
.idx
== kInvalidIdx
||
53 m_fde
!= kInvalidIdx
);
57 void EHFrameWriter::dw_op(uint8_t op
) {
58 // Only allow OP opcodes when we're writing an expression.
59 assertx(m_expr
!= kInvalidIdx
);
61 m_buf
->size() - (m_expr
+ sizeof(exprlen_t
)));
65 void EHFrameWriter::write_uleb(uint64_t value
) {
67 uint8_t byte
= value
& 0x7f;
70 if (value
!= 0) byte
|= 0x80;
75 void EHFrameWriter::write_sleb(int64_t value
) {
78 uint8_t byte
= value
& 0x7f;
81 more
= !(value
== 0 && (byte
& 0x40) == 0) &&
82 !(value
== -1 && (byte
& 0x40) != 0);
84 if (more
) byte
|= 0x80;
89 ///////////////////////////////////////////////////////////////////////////////
91 EHFrameDesc::~EHFrameDesc() {
92 if (m_buf
== nullptr) return;
94 for_each_fde([] (const FDE
& fde
) {
95 __deregister_frame(fde
.get());
99 EHFrameDesc
EHFrameWriter::register_and_release() {
100 FTRACE(1, "Summary:\n");
103 eh
.m_buf
= std::move(m_buf
);
105 assertx(m_cie
.idx
== 0);
106 FTRACE(1, " [{}] CIE length={}\n", eh
.cie().get(), eh
.cie().length());
108 // Register all the FDEs.
109 eh
.for_each_fde([&] (const EHFrameDesc::FDE
& fde
) {
110 DEBUG_ONLY
auto const cie_off
= (fde
.get() + 4) - fde
.cie_off();
112 assertx(eh
.cie().get() == cie_off
);
113 FTRACE(1, " [{}] FDE length={} cie=[{}]\n",
114 fde
.get(), fde
.length(), cie_off
);
116 __register_frame(fde
.get());
123 ///////////////////////////////////////////////////////////////////////////////
125 void EHFrameWriter::begin_cie(uint8_t rip
, const void* personality
) {
126 assertx(m_buf
!= nullptr && m_buf
->size() == 0);
127 assertx(m_cie
.idx
== kInvalidIdx
);
129 write
<uint32_t>(0); // length; to be rewritten later
130 write
<uint32_t>(0); // CIE_id
131 write
<uint8_t>(1); // version
133 // Null-terminated augmentation string.
136 if (personality
) write
<char>('P');
139 // Code and data alignment factors.
142 m_cie
.data_align
= -8;
144 // Return address register. (One byte; this appears to be undocumented.)
147 // Augmentation data length. We compute this ahead-of-time because it has to
148 // be encoded as a ULEB128, which is variable size.
149 auto const ad_len
= personality
150 ? sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uintptr_t)
154 DEBUG_ONLY
auto const ad_start
= m_buf
->size();
156 // FDE address format.
157 write
<uint8_t>(DW_EH_PE_absptr
);
159 // Personality routine address format and address.
160 if (personality
!= nullptr) {
161 write
<uint8_t>(DW_EH_PE_absptr
);
162 write
<uintptr_t>(uintptr_t(personality
));
164 assertx(ad_len
== m_buf
->size() - ad_start
);
170 " augmentation: \"{}\"\n"
171 " code_alignment_factor: 1\n"
172 " data_alignment_factor: -8\n"
173 " return_address_register: {}\n"
174 " Augmentation data: 0x{:0x} (FDE address encoding: absptr)\n"
177 personality
? "zRP" : "zR",
183 void EHFrameWriter::end_cie() {
184 assertx(m_buf
!= nullptr && m_buf
->size() != 0);
187 // Patch the length field. Note that it doesn't count the space for itself.
188 auto vp
= reinterpret_cast<uint32_t*>(&vec
[0]);
189 *vp
= vec
.size() - sizeof(uint32_t);
195 void EHFrameWriter::begin_fde(CodeAddress start
) {
196 assertx(m_buf
!= nullptr);
197 assertx(m_cie
.idx
!= kInvalidIdx
);
198 assertx(m_fde
== kInvalidIdx
);
203 // Reserve space for the FDE length.
206 // Negative offset to the CIE, relative to this field.
208 // It's not completely clear whether all the FDEs need to follow a CIE
209 // contiguously, or merely be contiguous with one another, but we use an
210 // unsigned value because that's what the docs say (even though libgcc seems
211 // to use a signed value).
212 auto const cie_off
= uint32_t(vec
.size() - m_cie
.idx
);
213 write
<uint32_t>(cie_off
);
215 // 8-byte pointer and 8-byte size describing the code range for this FDE.
216 write
<CodeAddress
>(start
);
217 write
<size_t>(0); // patched later
219 // Augmentation data length.
222 // Set the CFI table row address to the start address, and reset the CFA
223 // offset to what it was at the end of the CIE.
225 m_off
= m_cie
.offset
;
230 " initial_location: {}\n"
238 void EHFrameWriter::end_fde(size_t size
) {
239 assertx(m_buf
!= nullptr && m_buf
->size() != 0);
240 assertx(m_fde
!= kInvalidIdx
&& m_fde
< m_buf
->size());
243 // Patch the length field. Note that it doesn't count the space for itself.
244 auto vp_len
= reinterpret_cast<uint32_t*>(&vec
[m_fde
]);
245 *vp_len
= vec
.size() - m_fde
- sizeof(uint32_t);
247 // Patch the PC range field.
248 auto const idx
= m_fde
249 + sizeof(uint32_t) // FDE length
250 + sizeof(int32_t) // CIE_pointer
251 + sizeof(CodeAddress
); // PC start
252 auto vp_range
= reinterpret_cast<size_t*>(&vec
[idx
]);
255 // Register that we're no longer writing.
260 " address_range: 0x{:0x}\n"
266 void EHFrameWriter::null_fde() {
267 assertx(m_fde
== kInvalidIdx
);
271 ///////////////////////////////////////////////////////////////////////////////
273 static bool operand_fits(uint64_t operand
) {
274 return operand
== (operand
& 0x3f);
277 void EHFrameWriter::advance_loc_to(CodeAddress addr
) {
278 if (m_loc
== nullptr) {
279 FTRACE(1, " set_loc to {}\n", m_loc
);
281 dw_cfa(DW_CFA_set_loc
);
282 write
<CodeAddress
>(m_loc
);
285 assertx(addr
> m_loc
);
287 auto const off
= addr
- m_loc
;
288 FTRACE(1, " advance_loc {} to {}\n", off
, addr
);
290 if (operand_fits(off
)) {
291 dw_cfa(DW_CFA_advance_loc
| off
);
292 } else if (off
== uint8_t(off
)) {
293 dw_cfa(DW_CFA_advance_loc1
);
295 } else if (off
== uint16_t(off
)) {
296 dw_cfa(DW_CFA_advance_loc2
);
297 write
<uint16_t>(off
);
298 } else if (off
== uint32_t(off
)) {
299 dw_cfa(DW_CFA_advance_loc4
);
300 write
<uint32_t>(off
);
302 dw_cfa(DW_CFA_set_loc
);
303 write
<CodeAddress
>(m_loc
);
308 void EHFrameWriter::def_cfa(uint8_t reg
, uint64_t off
) {
309 FTRACE(1, " def_cfa r{} at offset {}\n", reg
, off
);
311 dw_cfa(DW_CFA_def_cfa
);
315 if (m_fde
== kInvalidIdx
) {
321 void EHFrameWriter::def_cfa_register(uint8_t reg
) {
322 FTRACE(1, " def_cfa_register r{}\n", reg
);
323 dw_cfa(DW_CFA_def_cfa_register
);
327 void EHFrameWriter::def_cfa_offset(uint64_t off
) {
328 FTRACE(1, " def_cfa_offset {}\n", off
);
329 dw_cfa(DW_CFA_def_cfa_offset
);
333 void EHFrameWriter::advance_cfa_offset(int64_t off
) {
335 def_cfa_offset(m_off
);
338 void EHFrameWriter::same_value(uint8_t reg
) {
339 FTRACE(1, " same_value r{}\n", reg
);
340 dw_cfa(DW_CFA_same_value
);
344 void EHFrameWriter::offset(uint8_t reg
, uint64_t off
) {
345 FTRACE(1, " offset r{} at cfa{}\n", reg
, off
* m_cie
.data_align
);
347 if (operand_fits(reg
)) {
348 dw_cfa(DW_CFA_offset
| reg
);
350 dw_cfa(DW_CFA_offset_extended
);
356 void EHFrameWriter::offset_extended_sf(uint8_t reg
, int64_t off
) {
357 FTRACE(1, " offset r{} at cfa{}\n", reg
, off
* m_cie
.data_align
);
358 dw_cfa(DW_CFA_offset_extended_sf
);
363 void EHFrameWriter::restore(uint8_t reg
) {
364 FTRACE(1, " restore r{}\n", reg
);
365 if (operand_fits(reg
)) {
366 dw_cfa(DW_CFA_restore
| reg
);
368 dw_cfa(DW_CFA_restore_extended
);
373 void EHFrameWriter::nop() {
378 ///////////////////////////////////////////////////////////////////////////////
380 void EHFrameWriter::begin_expr_impl(uint8_t op
, uint8_t reg
) {
381 assertx(m_buf
!= nullptr);
382 assertx(m_expr
== kInvalidIdx
);
387 // Reserve space for the expression length and mark the beginning of the
388 // expression, so that the size can be patched on end_expression().
389 m_expr
= m_buf
->size();
393 void EHFrameWriter::begin_expression(uint8_t reg
) {
394 FTRACE(1, " expression r{}\n", reg
);
395 begin_expr_impl(DW_CFA_expression
, reg
);
398 void EHFrameWriter::begin_val_expression(uint8_t reg
) {
399 FTRACE(1, " val_expression r{}\n", reg
);
400 begin_expr_impl(DW_CFA_val_expression
, reg
);
403 void EHFrameWriter::end_expression() {
404 assertx(m_buf
!= nullptr && m_buf
->size() != 0);
405 assertx(m_expr
!= kInvalidIdx
&& m_expr
< m_buf
->size());
408 // Patch the length field. Note that it doesn't count the space for itself.
409 auto vp_len
= reinterpret_cast<exprlen_t
*>(&vec
[m_expr
]);
410 auto const expr_len
= vec
.size() - m_expr
- sizeof(exprlen_t
);
411 assertx(expr_len
< std::numeric_limits
<exprlen_t
>::max());
414 // Register that we are no longer writing.
415 m_expr
= kInvalidIdx
;
418 ///////////////////////////////////////////////////////////////////////////////
420 void EHFrameWriter::op_consts(int64_t cns
) {
422 FTRACE(1, "consts {}\n", cns
);
426 void EHFrameWriter::op_breg(uint8_t reg
, int64_t off
) {
428 FTRACE(1, "bregx r{} {}\n", reg
, off
);
433 #define IMPL_STACK_OP(name) \
434 void EHFrameWriter::op_##name() { \
435 dw_op(DW_OP_##name); \
436 FTRACE(1, #name "\n"); \
457 ///////////////////////////////////////////////////////////////////////////////