blob: 3a113ffea29cae7179c1aff64349cb2a4f5540dd (
plain) (
blame)
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
// SPDX-License-Identifier: GPL-2.0
/*
* C++ exceptions. llvm lowers unwinding to two insns:
* invoke foo unwind pad call foo; unwind_goto pad
* resume r0 = 0; unwind_exit
* so 'throw' is a jump or an exit, 'catch (...)' and the destructors that run on
* the way are ordinary blocks.
*
* vmlinux.h and bpf_helpers.h are not C++.
*/
#define SEC(name) __attribute__((section(name), used))
#define __ksym __attribute__((section(".ksyms")))
#define __noinline __attribute__((noinline))
#define __always_inline inline __attribute__((always_inline))
#ifdef __BPF_FEATURE_UNWIND
struct __sk_buff {
unsigned int len;
};
extern "C" {
void bpf_rcu_read_lock(void) noexcept __ksym;
void bpf_rcu_read_unlock(void) noexcept __ksym;
void bpf_preempt_disable(void) noexcept __ksym;
void bpf_preempt_enable(void) noexcept __ksym;
/* Starts an unwind. Not a kfunc: llvm lowers the call and it doesn't come back. */
int _Unwind_RaiseException(void *exc);
/* All that is left of the C++ runtime. Nobody looks at the exception object. */
static char exc_obj[16];
__always_inline void *__cxa_allocate_exception(unsigned long size) noexcept
{
return exc_obj;
}
__always_inline void __cxa_throw(void *exc, void *tinfo, void (*dtor)(void *))
{
_Unwind_RaiseException(exc);
}
__always_inline void *__cxa_begin_catch(void *exc) noexcept
{
return exc;
}
__always_inline void __cxa_end_catch(void) noexcept
{
}
}
/* One decimal digit per destructor, in the order they ran */
volatile int dtors SEC(".data.dtors");
/* The verifier insists that both are undone on every path out of the prog */
struct rcu_guard {
__always_inline rcu_guard()
{
bpf_rcu_read_lock();
}
__always_inline ~rcu_guard()
{
bpf_rcu_read_unlock();
dtors = dtors * 10 + 1;
}
};
struct plain_rcu_guard {
__always_inline plain_rcu_guard()
{
bpf_rcu_read_lock();
}
__always_inline ~plain_rcu_guard()
{
bpf_rcu_read_unlock();
}
};
struct preempt_guard {
__always_inline preempt_guard()
{
bpf_preempt_disable();
}
__always_inline ~preempt_guard()
{
bpf_preempt_enable();
dtors = dtors * 10 + 2;
}
};
static __noinline int inner(int len)
{
rcu_guard guard;
if (len > 100)
throw 1;
return len;
}
static __noinline int middle(int len)
{
preempt_guard guard;
return inner(len) + 1;
}
using u128 = unsigned __int128;
/* Global subprogs returning 16 bytes use R0:R2, including unwind exits. */
extern "C" __noinline u128 aggregate_return(int len)
{
plain_rcu_guard guard;
if (len > 100)
throw 1;
return (static_cast<u128>(len) << 64) | 7;
}
extern "C" SEC("tc") int catches(struct __sk_buff *skb)
{
try {
return middle(skb->len);
} catch (...) {
return 42;
}
}
/* The exception leaves the prog: it returns 0 */
extern "C" SEC("tc") int does_not_catch(struct __sk_buff *skb)
{
return middle(skb->len);
}
extern "C" SEC("tc") int catches_aggregate(struct __sk_buff *skb)
{
try {
u128 value = aggregate_return(skb->len);
return static_cast<unsigned long>(value) +
static_cast<unsigned long>(value >> 64);
} catch (...) {
return 42;
}
}
#endif
char _license[] SEC("license") = "GPL";
|