1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
#![feature(link_llvm_intrinsics)]
#![allow(non_camel_case_types)]
#![cfg(not(windows))]
extern crate libc;
use libc::{SIGUSR1, SIGALRM, SIGHUP, SIGQUIT, SIGTERM};
use std::mem;
const JMP_BUF_WIDTH: usize = mem::size_of::<usize>() * 8;
type jmp_buf = [i8; JMP_BUF_WIDTH];
static mut SHUT_DOWN: bool = false;
static mut RETURN_HERE: jmp_buf = [0; JMP_BUF_WIDTH];
const MOCK_SIGNAL_AT: usize = 3;
extern "C" {
#[link_name = "llvm.setjmp"]
pub fn setjmp(a: *mut i8) -> i32;
#[link_name = "llvm.longjmp"]
pub fn longjmp(a: *mut i8, b: i32) -> ();
}
#[inline]
fn ptr_to_jmp_buf() -> *mut i8 {
unsafe { &RETURN_HERE as *const i8 as *mut i8 }
}
#[inline]
fn return_early() {
let franken_pointer = ptr_to_jmp_buf();
unsafe { longjmp(franken_pointer, 1) };
}
fn register_signal_handler() {
unsafe {
libc::signal(SIGUSR1, handle_signals as usize); // <4>
}
}
#[allow(dead_code)] // <5>
fn handle_signals(sig: i32) {
register_signal_handler(); // Immediately re-registering the signal handler minimizes the chances of a missing a signal.
let should_shut_down = match sig {
SIGHUP => false, // <7>
SIGALRM => false, // <7>
SIGTERM => true, // <7>
SIGQUIT => true, // <7>
SIGUSR1 => true,
_ => false, // <7>
};
unsafe {
// This unsafe block is required because we are modifying a global static variable.
SHUT_DOWN = should_shut_down;
}
return_early();
}
fn print_depth(depth: usize) {
for _ in 0..depth {
print!("#");
}
println!("");
}
fn dive(depth: usize, max_depth: usize) {
unsafe {
if SHUT_DOWN == true {
println!("!");
return;
}
}
print_depth(depth);
if depth >= max_depth {
return;
} else if depth == MOCK_SIGNAL_AT {
unsafe {
libc::raise(SIGUSR1);
}
} else {
dive(depth + 1, max_depth);
}
print_depth(depth);
}
fn main() {
const JUMP_SET: i32 = 0;
register_signal_handler();
let return_point = ptr_to_jmp_buf();
let rc = unsafe { setjmp(return_point) };
if rc == JUMP_SET {
dive(0, 10);
} else {
println!("early return!");
}
println!("finishing!")
}
|