]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/esan/esan_interceptors.cpp
Merge ACPICA 20180105.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / esan / esan_interceptors.cpp
1 //===-- esan_interceptors.cpp ---------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of EfficiencySanitizer, a family of performance tuners.
11 //
12 // Interception routines for the esan run-time.
13 //===----------------------------------------------------------------------===//
14
15 #include "esan.h"
16 #include "esan_shadow.h"
17 #include "interception/interception.h"
18 #include "sanitizer_common/sanitizer_common.h"
19 #include "sanitizer_common/sanitizer_libc.h"
20 #include "sanitizer_common/sanitizer_linux.h"
21 #include "sanitizer_common/sanitizer_stacktrace.h"
22
23 using namespace __esan; // NOLINT
24
25 #define CUR_PC() (StackTrace::GetCurrentPc())
26
27 //===----------------------------------------------------------------------===//
28 // Interception via sanitizer common interceptors
29 //===----------------------------------------------------------------------===//
30
31 // Get the per-platform defines for what is possible to intercept
32 #include "sanitizer_common/sanitizer_platform_interceptors.h"
33
34 DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
35
36 // TODO(bruening): tsan disables several interceptors (getpwent, etc.) claiming
37 // that interception is a perf hit: should we do the same?
38
39 // We have no need to intercept:
40 #undef SANITIZER_INTERCEPT_TLS_GET_ADDR
41
42 // TODO(bruening): the common realpath interceptor assumes malloc is
43 // intercepted!  We should try to parametrize that, though we'll
44 // intercept malloc soon ourselves and can then remove this undef.
45 #undef SANITIZER_INTERCEPT_REALPATH
46
47 // We provide our own version:
48 #undef SANITIZER_INTERCEPT_SIGPROCMASK
49
50 #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!EsanIsInitialized)
51
52 #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
53 #define COMMON_INTERCEPT_FUNCTION_VER(name, ver)                          \
54   INTERCEPT_FUNCTION_VER(name, ver)
55
56 // We must initialize during early interceptors, to support tcmalloc.
57 // This means that for some apps we fully initialize prior to
58 // __esan_init() being called.
59 // We currently do not use ctx.
60 #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...)                               \
61   do {                                                                         \
62     if (UNLIKELY(COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)) {                 \
63       if (!UNLIKELY(EsanDuringInit))                                           \
64         initializeLibrary(__esan_which_tool);                                  \
65       return REAL(func)(__VA_ARGS__);                                          \
66     }                                                                          \
67     ctx = nullptr;                                                             \
68     (void)ctx;                                                                 \
69   } while (false)
70
71 #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...)                      \
72   COMMON_INTERCEPTOR_ENTER(ctx, func, __VA_ARGS__)
73
74 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size)                         \
75   processRangeAccess(CUR_PC(), (uptr)ptr, size, true)
76
77 #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size)                          \
78   processRangeAccess(CUR_PC(), (uptr)ptr, size, false)
79
80 // This is only called if the app explicitly calls exit(), not on
81 // a normal exit.
82 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) finalizeLibrary()
83
84 #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path)                          \
85   do {                                                                         \
86     (void)(ctx);                                                               \
87     (void)(file);                                                              \
88     (void)(path);                                                              \
89   } while (false)
90 #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file)                               \
91   do {                                                                         \
92     (void)(ctx);                                                               \
93     (void)(file);                                                              \
94   } while (false)
95 #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle)                    \
96   do {                                                                         \
97     (void)(filename);                                                          \
98     (void)(handle);                                                            \
99   } while (false)
100 #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED()                                  \
101   do {                                                                         \
102   } while (false)
103 #define COMMON_INTERCEPTOR_ACQUIRE(ctx, u)                                     \
104   do {                                                                         \
105     (void)(ctx);                                                               \
106     (void)(u);                                                                 \
107   } while (false)
108 #define COMMON_INTERCEPTOR_RELEASE(ctx, u)                                     \
109   do {                                                                         \
110     (void)(ctx);                                                               \
111     (void)(u);                                                                 \
112   } while (false)
113 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path)                              \
114   do {                                                                         \
115     (void)(ctx);                                                               \
116     (void)(path);                                                              \
117   } while (false)
118 #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd)                                 \
119   do {                                                                         \
120     (void)(ctx);                                                               \
121     (void)(fd);                                                                \
122   } while (false)
123 #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd)                                 \
124   do {                                                                         \
125     (void)(ctx);                                                               \
126     (void)(fd);                                                                \
127   } while (false)
128 #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd)                                  \
129   do {                                                                         \
130     (void)(ctx);                                                               \
131     (void)(fd);                                                                \
132   } while (false)
133 #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd)                    \
134   do {                                                                         \
135     (void)(ctx);                                                               \
136     (void)(fd);                                                                \
137     (void)(newfd);                                                             \
138   } while (false)
139 #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name)                          \
140   do {                                                                         \
141     (void)(ctx);                                                               \
142     (void)(name);                                                              \
143   } while (false)
144 #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name)                 \
145   do {                                                                         \
146     (void)(ctx);                                                               \
147     (void)(thread);                                                            \
148     (void)(name);                                                              \
149   } while (false)
150 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
151 #define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m)                                  \
152   do {                                                                         \
153     (void)(ctx);                                                               \
154     (void)(m);                                                                 \
155   } while (false)
156 #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m)                                \
157   do {                                                                         \
158     (void)(ctx);                                                               \
159     (void)(m);                                                                 \
160   } while (false)
161 #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m)                                \
162   do {                                                                         \
163     (void)(ctx);                                                               \
164     (void)(m);                                                                 \
165   } while (false)
166 #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg)                            \
167   do {                                                                         \
168     (void)(ctx);                                                               \
169     (void)(msg);                                                               \
170   } while (false)
171 #define COMMON_INTERCEPTOR_USER_CALLBACK_START()                               \
172   do {                                                                         \
173   } while (false)
174 #define COMMON_INTERCEPTOR_USER_CALLBACK_END()                                 \
175   do {                                                                         \
176   } while (false)
177
178 #include "sanitizer_common/sanitizer_common_interceptors.inc"
179
180 //===----------------------------------------------------------------------===//
181 // Syscall interception
182 //===----------------------------------------------------------------------===//
183
184 // We want the caller's PC b/c unlike the other function interceptors these
185 // are separate pre and post functions called around the app's syscall().
186
187 #define COMMON_SYSCALL_PRE_READ_RANGE(ptr, size)                               \
188   processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, false)
189
190 #define COMMON_SYSCALL_PRE_WRITE_RANGE(ptr, size)                              \
191   do {                                                                         \
192     (void)(ptr);                                                               \
193     (void)(size);                                                              \
194   } while (false)
195
196 #define COMMON_SYSCALL_POST_READ_RANGE(ptr, size)                              \
197   do {                                                                         \
198     (void)(ptr);                                                               \
199     (void)(size);                                                              \
200   } while (false)
201
202 // The actual amount written is in post, not pre.
203 #define COMMON_SYSCALL_POST_WRITE_RANGE(ptr, size)                             \
204   processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, true)
205
206 #define COMMON_SYSCALL_ACQUIRE(addr)                                           \
207   do {                                                                         \
208     (void)(addr);                                                              \
209   } while (false)
210 #define COMMON_SYSCALL_RELEASE(addr)                                           \
211   do {                                                                         \
212     (void)(addr);                                                              \
213   } while (false)
214 #define COMMON_SYSCALL_FD_CLOSE(fd)                                            \
215   do {                                                                         \
216     (void)(fd);                                                                \
217   } while (false)
218 #define COMMON_SYSCALL_FD_ACQUIRE(fd)                                          \
219   do {                                                                         \
220     (void)(fd);                                                                \
221   } while (false)
222 #define COMMON_SYSCALL_FD_RELEASE(fd)                                          \
223   do {                                                                         \
224     (void)(fd);                                                                \
225   } while (false)
226 #define COMMON_SYSCALL_PRE_FORK()                                              \
227   do {                                                                         \
228   } while (false)
229 #define COMMON_SYSCALL_POST_FORK(res)                                          \
230   do {                                                                         \
231     (void)(res);                                                               \
232   } while (false)
233
234 #include "sanitizer_common/sanitizer_common_syscalls.inc"
235
236 //===----------------------------------------------------------------------===//
237 // Custom interceptors
238 //===----------------------------------------------------------------------===//
239
240 // TODO(bruening): move more of these to the common interception pool as they
241 // are shared with tsan and asan.
242 // While our other files match LLVM style, here we match sanitizer style as we
243 // expect to move these to the common pool.
244
245 INTERCEPTOR(char *, strcpy, char *dst, const char *src) { // NOLINT
246   void *ctx;
247   COMMON_INTERCEPTOR_ENTER(ctx, strcpy, dst, src);
248   uptr srclen = internal_strlen(src);
249   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, srclen + 1);
250   COMMON_INTERCEPTOR_READ_RANGE(ctx, src, srclen + 1);
251   return REAL(strcpy)(dst, src); // NOLINT
252 }
253
254 INTERCEPTOR(char *, strncpy, char *dst, char *src, uptr n) {
255   void *ctx;
256   COMMON_INTERCEPTOR_ENTER(ctx, strncpy, dst, src, n);
257   uptr srclen = internal_strnlen(src, n);
258   uptr copied_size = srclen + 1 > n ? n : srclen + 1;
259   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, copied_size);
260   COMMON_INTERCEPTOR_READ_RANGE(ctx, src, copied_size);
261   return REAL(strncpy)(dst, src, n);
262 }
263
264 INTERCEPTOR(int, open, const char *name, int flags, int mode) {
265   void *ctx;
266   COMMON_INTERCEPTOR_ENTER(ctx, open, name, flags, mode);
267   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
268   return REAL(open)(name, flags, mode);
269 }
270
271 #if SANITIZER_LINUX
272 INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
273   void *ctx;
274   COMMON_INTERCEPTOR_ENTER(ctx, open64, name, flags, mode);
275   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
276   return REAL(open64)(name, flags, mode);
277 }
278 #define ESAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64)
279 #else
280 #define ESAN_MAYBE_INTERCEPT_OPEN64
281 #endif
282
283 INTERCEPTOR(int, creat, const char *name, int mode) {
284   void *ctx;
285   COMMON_INTERCEPTOR_ENTER(ctx, creat, name, mode);
286   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
287   return REAL(creat)(name, mode);
288 }
289
290 #if SANITIZER_LINUX
291 INTERCEPTOR(int, creat64, const char *name, int mode) {
292   void *ctx;
293   COMMON_INTERCEPTOR_ENTER(ctx, creat64, name, mode);
294   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
295   return REAL(creat64)(name, mode);
296 }
297 #define ESAN_MAYBE_INTERCEPT_CREAT64 INTERCEPT_FUNCTION(creat64)
298 #else
299 #define ESAN_MAYBE_INTERCEPT_CREAT64
300 #endif
301
302 INTERCEPTOR(int, unlink, char *path) {
303   void *ctx;
304   COMMON_INTERCEPTOR_ENTER(ctx, unlink, path);
305   COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
306   return REAL(unlink)(path);
307 }
308
309 INTERCEPTOR(int, puts, const char *s) {
310   void *ctx;
311   COMMON_INTERCEPTOR_ENTER(ctx, puts, s);
312   COMMON_INTERCEPTOR_READ_RANGE(ctx, s, internal_strlen(s));
313   return REAL(puts)(s);
314 }
315
316 INTERCEPTOR(int, rmdir, char *path) {
317   void *ctx;
318   COMMON_INTERCEPTOR_ENTER(ctx, rmdir, path);
319   COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
320   return REAL(rmdir)(path);
321 }
322
323 //===----------------------------------------------------------------------===//
324 // Shadow-related interceptors
325 //===----------------------------------------------------------------------===//
326
327 // These are candidates for sharing with all sanitizers if shadow memory
328 // support is also standardized.
329
330 INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags,
331                  int fd, OFF_T off) {
332   if (UNLIKELY(REAL(mmap) == nullptr)) {
333     // With esan init during interceptor init and a static libc preventing
334     // our early-calloc from triggering, we can end up here before our
335     // REAL pointer is set up.
336     return (void *)internal_mmap(addr, sz, prot, flags, fd, off);
337   }
338   void *ctx;
339   COMMON_INTERCEPTOR_ENTER(ctx, mmap, addr, sz, prot, flags, fd, off);
340   if (!fixMmapAddr(&addr, sz, flags))
341     return (void *)-1;
342   void *result = REAL(mmap)(addr, sz, prot, flags, fd, off);
343   return (void *)checkMmapResult((uptr)result, sz);
344 }
345
346 #if SANITIZER_LINUX
347 INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags,
348                  int fd, OFF64_T off) {
349   void *ctx;
350   COMMON_INTERCEPTOR_ENTER(ctx, mmap64, addr, sz, prot, flags, fd, off);
351   if (!fixMmapAddr(&addr, sz, flags))
352     return (void *)-1;
353   void *result = REAL(mmap64)(addr, sz, prot, flags, fd, off);
354   return (void *)checkMmapResult((uptr)result, sz);
355 }
356 #define ESAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64)
357 #else
358 #define ESAN_MAYBE_INTERCEPT_MMAP64
359 #endif
360
361 //===----------------------------------------------------------------------===//
362 // Signal-related interceptors
363 //===----------------------------------------------------------------------===//
364
365 #if SANITIZER_LINUX
366 typedef void (*signal_handler_t)(int);
367 INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
368   void *ctx;
369   COMMON_INTERCEPTOR_ENTER(ctx, signal, signum, handler);
370   signal_handler_t result;
371   if (!processSignal(signum, handler, &result))
372     return result;
373   else
374     return REAL(signal)(signum, handler);
375 }
376 #define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal)
377 #else
378 #error Platform not supported
379 #define ESAN_MAYBE_INTERCEPT_SIGNAL
380 #endif
381
382 #if SANITIZER_LINUX
383 DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
384              struct sigaction *oldact)
385 INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
386             struct sigaction *oldact) {
387   void *ctx;
388   COMMON_INTERCEPTOR_ENTER(ctx, sigaction, signum, act, oldact);
389   if (!processSigaction(signum, act, oldact))
390     return 0;
391   else
392     return REAL(sigaction)(signum, act, oldact);
393 }
394
395 // This is required to properly use internal_sigaction.
396 namespace __sanitizer {
397 int real_sigaction(int signum, const void *act, void *oldact) {
398   if (REAL(sigaction) == nullptr) {
399     // With an instrumented allocator, this is called during interceptor init
400     // and we need a raw syscall solution.
401     return internal_sigaction_syscall(signum, act, oldact);
402   }
403   return REAL(sigaction)(signum, (const struct sigaction *)act,
404                          (struct sigaction *)oldact);
405 }
406 } // namespace __sanitizer
407
408 #define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction)
409 #else
410 #error Platform not supported
411 #define ESAN_MAYBE_INTERCEPT_SIGACTION
412 #endif
413
414 #if SANITIZER_LINUX
415 INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
416             __sanitizer_sigset_t *oldset) {
417   void *ctx;
418   COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
419   int res = 0;
420   if (processSigprocmask(how, set, oldset))
421     res = REAL(sigprocmask)(how, set, oldset);
422   if (!res && oldset)
423     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
424   return res;
425 }
426 #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask)
427 #else
428 #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK
429 #endif
430
431 #if !SANITIZER_WINDOWS
432 INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
433             __sanitizer_sigset_t *oldset) {
434   void *ctx;
435   COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset);
436   int res = 0;
437   if (processSigprocmask(how, set, oldset))
438     res = REAL(sigprocmask)(how, set, oldset);
439   if (!res && oldset)
440     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
441   return res;
442 }
443 #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask)
444 #else
445 #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
446 #endif
447
448 //===----------------------------------------------------------------------===//
449 // Malloc interceptors
450 //===----------------------------------------------------------------------===//
451
452 static const uptr early_alloc_buf_size = 4096;
453 static uptr allocated_bytes;
454 static char early_alloc_buf[early_alloc_buf_size];
455
456 static bool isInEarlyAllocBuf(const void *ptr) {
457   return ((uptr)ptr >= (uptr)early_alloc_buf &&
458           ((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
459 }
460
461 static void *handleEarlyAlloc(uptr size) {
462   // If esan is initialized during an interceptor (which happens with some
463   // tcmalloc implementations that call pthread_mutex_lock), the call from
464   // dlsym to calloc will deadlock.
465   // dlsym may also call malloc before REAL(malloc) is retrieved from dlsym.
466   // We work around it by using a static buffer for the early malloc/calloc
467   // requests.
468   // This solution will also allow us to deliberately intercept malloc & family
469   // in the future (to perform tool actions on each allocation, without
470   // replacing the allocator), as it also solves the problem of intercepting
471   // calloc when it will itself be called before its REAL pointer is
472   // initialized.
473   // We do not handle multiple threads here.  This only happens at process init
474   // time, and while it's possible for a shared library to create early threads
475   // that race here, we consider that to be a corner case extreme enough that
476   // it's not worth the effort to handle.
477   void *mem = (void *)&early_alloc_buf[allocated_bytes];
478   allocated_bytes += size;
479   CHECK_LT(allocated_bytes, early_alloc_buf_size);
480   return mem;
481 }
482
483 INTERCEPTOR(void*, calloc, uptr size, uptr n) {
484   if (EsanDuringInit && REAL(calloc) == nullptr)
485     return handleEarlyAlloc(size * n);
486   void *ctx;
487   COMMON_INTERCEPTOR_ENTER(ctx, calloc, size, n);
488   void *res = REAL(calloc)(size, n);
489   // The memory is zeroed and thus is all written.
490   COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr)res, size * n);
491   return res;
492 }
493
494 INTERCEPTOR(void*, malloc, uptr size) {
495   if (EsanDuringInit && REAL(malloc) == nullptr)
496     return handleEarlyAlloc(size);
497   void *ctx;
498   COMMON_INTERCEPTOR_ENTER(ctx, malloc, size);
499   return REAL(malloc)(size);
500 }
501
502 INTERCEPTOR(void, free, void *p) {
503   void *ctx;
504   // There are only a few early allocation requests, so we simply skip the free.
505   if (isInEarlyAllocBuf(p))
506     return;
507   COMMON_INTERCEPTOR_ENTER(ctx, free, p);
508   REAL(free)(p);
509 }
510
511 namespace __esan {
512
513 void initializeInterceptors() {
514   InitializeCommonInterceptors();
515
516   INTERCEPT_FUNCTION(strcpy); // NOLINT
517   INTERCEPT_FUNCTION(strncpy);
518
519   INTERCEPT_FUNCTION(open);
520   ESAN_MAYBE_INTERCEPT_OPEN64;
521   INTERCEPT_FUNCTION(creat);
522   ESAN_MAYBE_INTERCEPT_CREAT64;
523   INTERCEPT_FUNCTION(unlink);
524   INTERCEPT_FUNCTION(fread);
525   INTERCEPT_FUNCTION(fwrite);
526   INTERCEPT_FUNCTION(puts);
527   INTERCEPT_FUNCTION(rmdir);
528
529   INTERCEPT_FUNCTION(mmap);
530   ESAN_MAYBE_INTERCEPT_MMAP64;
531
532   ESAN_MAYBE_INTERCEPT_SIGNAL;
533   ESAN_MAYBE_INTERCEPT_SIGACTION;
534   ESAN_MAYBE_INTERCEPT_SIGPROCMASK;
535   ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
536
537   INTERCEPT_FUNCTION(calloc);
538   INTERCEPT_FUNCTION(malloc);
539   INTERCEPT_FUNCTION(free);
540
541   // TODO(bruening): intercept routines that other sanitizers intercept that
542   // are not in the common pool or here yet, ideally by adding to the common
543   // pool.  Examples include wcslen and bcopy.
544
545   // TODO(bruening): there are many more libc routines that read or write data
546   // structures that no sanitizer is intercepting: sigaction, strtol, etc.
547 }
548
549 } // namespace __esan