Fix semdiff syntactic output
[hiphop-php.git] / hphp / util / eh-frame.cpp
bloba11738ce3fe1288bc0235a13d28dc51c1c042a95
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
24 #include <cstring>
25 #include <limits>
26 #include <memory>
27 #include <vector>
29 #ifdef _MSC_VER
30 void __register_frame(const void*) {}
31 void __deregister_frame(const void*) {}
32 #else
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
36 * .eh_frame section.
38 extern "C" void __register_frame(const void*);
39 extern "C" void __deregister_frame(const void*);
40 #endif
42 namespace HPHP {
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);
54 write<uint8_t>(op);
57 void EHFrameWriter::dw_op(uint8_t op) {
58 // Only allow OP opcodes when we're writing an expression.
59 assertx(m_expr != kInvalidIdx);
60 FTRACE(1, " [{:4}] ",
61 m_buf->size() - (m_expr + sizeof(exprlen_t)));
62 write<uint8_t>(op);
65 void EHFrameWriter::write_uleb(uint64_t value) {
66 do {
67 uint8_t byte = value & 0x7f;
68 value >>= 7;
70 if (value != 0) byte |= 0x80;
71 write<uint8_t>(byte);
72 } while (value != 0);
75 void EHFrameWriter::write_sleb(int64_t value) {
76 bool more;
77 do {
78 uint8_t byte = value & 0x7f;
79 value >>= 7;
81 more = !(value == 0 && (byte & 0x40) == 0) &&
82 !(value == -1 && (byte & 0x40) != 0);
84 if (more) byte |= 0x80;
85 write<uint8_t>(byte);
86 } while (more);
89 ///////////////////////////////////////////////////////////////////////////////
91 EHFrameDesc::~EHFrameDesc() {
92 if (m_buf == nullptr) return;
94 for_each_fde([] (const FDE& fde) {
95 __deregister_frame(fde.get());
96 });
99 EHFrameDesc EHFrameWriter::register_and_release() {
100 FTRACE(1, "Summary:\n");
102 EHFrameDesc eh;
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());
119 FTRACE(1, "\n");
120 return eh;
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.
134 write<char>('z');
135 write<char>('R');
136 if (personality) write<char>('P');
137 write<char>('\0');
139 // Code and data alignment factors.
140 write_uleb(1);
141 write_sleb(-8);
142 m_cie.data_align = -8;
144 // Return address register. (One byte; this appears to be undocumented.)
145 write<uint8_t>(rip);
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)
151 : sizeof(uint8_t);
152 write_uleb(ad_len);
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);
166 FTRACE(1,
167 "[ v ] CIE\n"
168 " CIE_id: 0\n"
169 " version: 1\n"
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"
175 "\n"
176 " Program:\n",
177 personality ? "zRP" : "zR",
178 rip,
179 DW_EH_PE_absptr
183 void EHFrameWriter::end_cie() {
184 assertx(m_buf != nullptr && m_buf->size() != 0);
185 auto& vec = *m_buf;
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);
191 m_cie.idx = 0;
192 FTRACE(1, "\n");
195 void EHFrameWriter::begin_fde(CodeAddress start) {
196 assertx(m_buf != nullptr);
197 assertx(m_cie.idx != kInvalidIdx);
198 assertx(m_fde == kInvalidIdx);
199 auto& vec = *m_buf;
201 m_fde = vec.size();
203 // Reserve space for the FDE length.
204 write<uint32_t>(0);
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.
220 write_uleb(0);
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.
224 m_loc = start;
225 m_off = m_cie.offset;
227 FTRACE(1,
228 "[ v ] FDE\n"
229 " CIE_pointer: {}\n"
230 " initial_location: {}\n"
231 "\n"
232 " Program:\n",
233 cie_off,
234 start
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());
241 auto& vec = *m_buf;
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]);
253 *vp_range = size;
255 // Register that we're no longer writing.
256 m_fde = kInvalidIdx;
258 FTRACE(1,
259 "\n",
260 " address_range: 0x{:0x}\n"
261 "\n",
262 size
266 void EHFrameWriter::null_fde() {
267 assertx(m_fde == kInvalidIdx);
268 write<uint32_t>(0);
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);
280 m_loc = addr;
281 dw_cfa(DW_CFA_set_loc);
282 write<CodeAddress>(m_loc);
283 return;
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);
294 write<uint8_t>(off);
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);
301 } else {
302 dw_cfa(DW_CFA_set_loc);
303 write<CodeAddress>(m_loc);
305 m_loc = addr;
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);
312 write_uleb(reg);
313 write_uleb(off);
315 if (m_fde == kInvalidIdx) {
316 m_cie.offset = off;
318 m_off = off;
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);
324 write_uleb(reg);
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);
330 write_uleb(off);
333 void EHFrameWriter::advance_cfa_offset(int64_t off) {
334 m_off += 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);
341 write_uleb(reg);
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);
349 } else {
350 dw_cfa(DW_CFA_offset_extended);
351 write_uleb(reg);
353 write_uleb(off);
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);
359 write_uleb(reg);
360 write_sleb(off);
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);
367 } else {
368 dw_cfa(DW_CFA_restore_extended);
369 write_uleb(reg);
373 void EHFrameWriter::nop() {
374 FTRACE(1, " nop\n");
375 dw_cfa(DW_CFA_nop);
378 ///////////////////////////////////////////////////////////////////////////////
380 void EHFrameWriter::begin_expr_impl(uint8_t op, uint8_t reg) {
381 assertx(m_buf != nullptr);
382 assertx(m_expr == kInvalidIdx);
384 dw_cfa(op);
385 write_uleb(reg);
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();
390 write<exprlen_t>(0);
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());
406 auto& vec = *m_buf;
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());
412 *vp_len = expr_len;
414 // Register that we are no longer writing.
415 m_expr = kInvalidIdx;
418 ///////////////////////////////////////////////////////////////////////////////
420 void EHFrameWriter::op_consts(int64_t cns) {
421 dw_op(DW_OP_consts);
422 FTRACE(1, "consts {}\n", cns);
423 write_sleb(cns);
426 void EHFrameWriter::op_breg(uint8_t reg, int64_t off) {
427 dw_op(DW_OP_bregx);
428 FTRACE(1, "bregx r{} {}\n", reg, off);
429 write_uleb(reg);
430 write_sleb(off);
433 #define IMPL_STACK_OP(name) \
434 void EHFrameWriter::op_##name() { \
435 dw_op(DW_OP_##name); \
436 FTRACE(1, #name "\n"); \
439 IMPL_STACK_OP(dup)
440 IMPL_STACK_OP(drop)
441 IMPL_STACK_OP(swap)
442 IMPL_STACK_OP(deref)
444 IMPL_STACK_OP(abs)
445 IMPL_STACK_OP(and)
446 IMPL_STACK_OP(div)
447 IMPL_STACK_OP(minus)
448 IMPL_STACK_OP(mod)
449 IMPL_STACK_OP(mul)
450 IMPL_STACK_OP(neg)
451 IMPL_STACK_OP(not)
452 IMPL_STACK_OP(or)
453 IMPL_STACK_OP(plus)
455 #undef IMPL_STACK_OP
457 ///////////////////////////////////////////////////////////////////////////////