[ASan] Refactoring: nuke the redundant function declarations in asan_intercepted_functions.h
that had been used on OS X only. The INTERCEPTOR() macro on OS X is now responsible for declaring the wrapped function, the wrapper and the pair of pointers to them in __DATA,__interposition section. Thus adding an interceptor requires editing a single file now. llvm-svn: 175740
This commit is contained in:
parent
49498dac9d
commit
e8ba1c851a
|
@ -79,248 +79,11 @@ using __sanitizer::uptr;
|
|||
# define ASAN_INTERCEPT___CXA_THROW 0
|
||||
#endif
|
||||
|
||||
#define INTERPOSE_FUNCTION(function) \
|
||||
{ reinterpret_cast<const uptr>(WRAP(function)), \
|
||||
reinterpret_cast<const uptr>(function) }
|
||||
|
||||
#define INTERPOSE_FUNCTION_2(function, wrapper) \
|
||||
{ reinterpret_cast<const uptr>(wrapper), \
|
||||
reinterpret_cast<const uptr>(function) }
|
||||
|
||||
struct interpose_substitution {
|
||||
const uptr replacement;
|
||||
const uptr original;
|
||||
};
|
||||
|
||||
#define INTERPOSER(func) __attribute__((used)) \
|
||||
const interpose_substitution substitution_##func[] \
|
||||
__attribute__((section("__DATA, __interpose"))) = { \
|
||||
INTERPOSE_FUNCTION(func), \
|
||||
}
|
||||
|
||||
#define INTERPOSER_2(func, wrapper) __attribute__((used)) \
|
||||
const interpose_substitution substitution_##func[] \
|
||||
__attribute__((section("__DATA, __interpose"))) = { \
|
||||
INTERPOSE_FUNCTION_2(func, wrapper), \
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \
|
||||
ret_type func(__VA_ARGS__); \
|
||||
ret_type WRAP(func)(__VA_ARGS__); \
|
||||
INTERPOSER(func)
|
||||
|
||||
// Use extern declarations of intercepted functions on Mac and Windows
|
||||
// to avoid including system headers.
|
||||
#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL))
|
||||
extern "C" {
|
||||
// signal.h
|
||||
# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
||||
struct sigaction;
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig,
|
||||
const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler);
|
||||
# endif
|
||||
|
||||
// setjmp.h
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value);
|
||||
# if ASAN_INTERCEPT__LONGJMP
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value);
|
||||
# endif
|
||||
# if ASAN_INTERCEPT_SIGLONGJMP
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value);
|
||||
# endif
|
||||
# if ASAN_INTERCEPT___CXA_THROW
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c);
|
||||
# endif
|
||||
|
||||
// string.h / strings.h
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, memcmp,
|
||||
const void *a1, const void *a2, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void*, memmove,
|
||||
void *to, const void *from, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy,
|
||||
void *to, const void *from, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */
|
||||
char *to, const char* from);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char*, strncat,
|
||||
char *to, const char* from, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */
|
||||
char *to, const char* from);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy,
|
||||
char *to, const char* from, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, strncmp,
|
||||
const char *s1, const char* s2, uptr size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s);
|
||||
# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp,
|
||||
const char *s1, const char *s2, uptr n);
|
||||
# endif
|
||||
# if ASAN_INTERCEPT_STRDUP
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s);
|
||||
# endif
|
||||
# if ASAN_INTERCEPT_STRNLEN
|
||||
DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen);
|
||||
# endif
|
||||
# if ASAN_INTERCEPT_INDEX
|
||||
char* index(const char *string, int c);
|
||||
INTERPOSER_2(index, WRAP(strchr));
|
||||
# endif
|
||||
|
||||
// stdlib.h
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT
|
||||
DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT
|
||||
# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
|
||||
DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT
|
||||
DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT
|
||||
# endif
|
||||
|
||||
// unistd.h
|
||||
# if SANITIZER_INTERCEPT_READ
|
||||
DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count);
|
||||
# endif
|
||||
# if SANITIZER_INTERCEPT_PREAD
|
||||
DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf,
|
||||
SIZE_T count, OFF_T offset);
|
||||
# endif
|
||||
# if SANITIZER_INTERCEPT_PREAD64
|
||||
DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf,
|
||||
SIZE_T count, OFF64_T offset);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_INTERCEPT_WRITE
|
||||
DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count);
|
||||
# endif
|
||||
# if SANITIZER_INTERCEPT_PWRITE
|
||||
DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite,
|
||||
int fd, void *ptr, SIZE_T count, OFF_T offset);
|
||||
# endif
|
||||
|
||||
# if ASAN_INTERCEPT_MLOCKX
|
||||
// mlock/munlock
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void);
|
||||
# endif
|
||||
|
||||
// Windows threads.
|
||||
# if defined(_WIN32)
|
||||
__declspec(dllimport)
|
||||
void* __stdcall CreateThread(void *sec, uptr st, void* start,
|
||||
void *arg, DWORD fl, DWORD *id);
|
||||
# endif
|
||||
// Posix threads.
|
||||
# if ASAN_INTERCEPT_PTHREAD_CREATE
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create,
|
||||
void *thread, void *attr,
|
||||
void *(*start_routine)(void*), void *arg);
|
||||
# endif
|
||||
|
||||
# if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, localtime, unsigned long *timep);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, localtime_r, unsigned long *timep,
|
||||
void *result);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, gmtime, unsigned long *timep);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, gmtime_r, unsigned long *timep,
|
||||
void *result);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char *, ctime, unsigned long *timep);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char *, ctime_r, unsigned long *timep,
|
||||
char *result);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char *, asctime, void *tm);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(char *, asctime_r, void *tm, char *result);
|
||||
# endif
|
||||
|
||||
// stdio.h
|
||||
# if SANITIZER_INTERCEPT_SCANF
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format,
|
||||
va_list ap);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, vfscanf, void *stream, const char *format,
|
||||
va_list ap);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, scanf, const char *format, ...);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, fscanf,
|
||||
void* stream, const char *format, ...);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT
|
||||
const char *str, const char *format, ...);
|
||||
# endif
|
||||
|
||||
# if defined(__APPLE__)
|
||||
typedef void* pthread_workqueue_t;
|
||||
typedef void* pthread_workitem_handle_t;
|
||||
|
||||
typedef void* dispatch_group_t;
|
||||
typedef void* dispatch_queue_t;
|
||||
typedef void* dispatch_source_t;
|
||||
typedef u64 dispatch_time_t;
|
||||
typedef void (*dispatch_function_t)(void *block);
|
||||
typedef void* (*worker_t)(void *block);
|
||||
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f,
|
||||
dispatch_time_t when, dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f,
|
||||
dispatch_group_t group, dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
|
||||
# if !defined(MISSING_BLOCKS_SUPPORT)
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async,
|
||||
dispatch_group_t dg,
|
||||
dispatch_queue_t dq, void (^work)(void));
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async,
|
||||
dispatch_queue_t dq, void (^work)(void));
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after,
|
||||
dispatch_queue_t dq, void (^work)(void));
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler,
|
||||
dispatch_source_t ds, void (^work)(void));
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler,
|
||||
dispatch_source_t ds, void (^work)(void));
|
||||
# endif // MISSING_BLOCKS_SUPPORT
|
||||
|
||||
typedef void malloc_zone_t;
|
||||
typedef size_t vm_size_t;
|
||||
DECLARE_FUNCTION_AND_WRAPPER(malloc_zone_t *, malloc_create_zone,
|
||||
vm_size_t start_size, unsigned flags);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(malloc_zone_t *, malloc_default_zone, void);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(
|
||||
malloc_zone_t *, malloc_default_purgeable_zone, void);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, malloc_make_purgeable, void *ptr);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, malloc_make_nonpurgeable, void *ptr);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, malloc_set_zone_name,
|
||||
malloc_zone_t *zone, const char *name);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, malloc, size_t size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, free, void *ptr);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, realloc, void *ptr, size_t size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, calloc, size_t nmemb, size_t size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void *, valloc, size_t size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(size_t, malloc_good_size, size_t size);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(int, posix_memalign,
|
||||
void **memptr, size_t alignment, size_t size);
|
||||
#if 0
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_prepare, void);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_parent, void);
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_child, void);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
# endif // __APPLE__
|
||||
} // extern "C"
|
||||
#endif // defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL))
|
||||
|
||||
#endif // ASAN_INTERCEPTED_FUNCTIONS_H
|
||||
|
|
|
@ -341,9 +341,14 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
|
|||
#if ASAN_INTERCEPT_INDEX
|
||||
# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
|
||||
INTERCEPTOR(char*, index, const char *string, int c)
|
||||
ALIAS(WRAPPER_NAME(strchr));
|
||||
WEAK_ALIAS(WRAPPER_NAME(strchr));
|
||||
# else
|
||||
DEFINE_REAL(char*, index, const char *string, int c)
|
||||
# if defined(__APPLE__)
|
||||
DECLARE_REAL(char*, index, const char *string, int c)
|
||||
OVERRIDE_FUNCTION(index, strchr);
|
||||
# else
|
||||
DEFINE_REAL(char*, index, const char *string, int c);
|
||||
# endif
|
||||
# endif
|
||||
#endif // ASAN_INTERCEPT_INDEX
|
||||
|
||||
|
|
|
@ -288,24 +288,6 @@ typedef struct {
|
|||
u32 parent_tid;
|
||||
} asan_block_context_t;
|
||||
|
||||
// We use extern declarations of libdispatch functions here instead
|
||||
// of including <dispatch/dispatch.h>. This header is not present on
|
||||
// Mac OS X Leopard and eariler, and although we don't expect ASan to
|
||||
// work on legacy systems, it's bad to break the build of
|
||||
// LLVM compiler-rt there.
|
||||
extern "C" {
|
||||
void dispatch_async_f(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_sync_f(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
} // extern "C"
|
||||
|
||||
static ALWAYS_INLINE
|
||||
void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
|
||||
AsanThread *t = asanThreadRegistry().GetCurrent();
|
||||
|
|
|
@ -57,14 +57,14 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double)
|
||||
// to a header file.
|
||||
|
||||
// Notes: 1. Things may not work properly if macro INTERCEPT(...) {...} or
|
||||
// Notes: 1. Things may not work properly if macro INTERCEPTOR(...) {...} or
|
||||
// DECLARE_REAL(...) are located inside namespaces.
|
||||
// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo);" to
|
||||
// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo)" to
|
||||
// effectively redirect calls from "foo" to "zoo". In this case
|
||||
// you aren't required to implement
|
||||
// INTERCEPTOR(int, foo, const char *bar, double baz) {...}
|
||||
// but instead you'll have to add
|
||||
// DEFINE_REAL(int, foo, const char *bar, double baz) in your
|
||||
// DECLARE_REAL(int, foo, const char *bar, double baz) in your
|
||||
// source file (to define a pointer to overriden function).
|
||||
|
||||
// How it works:
|
||||
|
@ -75,6 +75,7 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
// we intercept. To resolve this we declare our interceptors with __interceptor_
|
||||
// prefix, and then make actual interceptors weak aliases to __interceptor_
|
||||
// functions.
|
||||
//
|
||||
// This is not so on Mac OS, where the two-level namespace makes
|
||||
// our replacement functions invisible to other libraries. This may be overcomed
|
||||
// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
|
||||
|
@ -84,12 +85,42 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all
|
||||
// the calls to interposed functions done through stubs to the wrapper
|
||||
// functions.
|
||||
// As it's decided at compile time which functions are to be intercepted on Mac,
|
||||
// INTERCEPT_FUNCTION() is effectively a no-op on this system.
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
// Just a pair of pointers.
|
||||
struct interpose_substitution {
|
||||
const uptr replacement;
|
||||
const uptr original;
|
||||
};
|
||||
|
||||
// For a function foo() create a global pair of pointers { wrap_foo, foo } in
|
||||
// the __DATA,__interpose section.
|
||||
// As a result all the calls to foo() will be routed to wrap_foo() at runtime.
|
||||
#define INTERPOSER(func_name) __attribute__((used)) \
|
||||
const interpose_substitution substitution_##func_name[] \
|
||||
__attribute__((section("__DATA, __interpose"))) = { \
|
||||
{ reinterpret_cast<const uptr>(WRAP(func_name)), \
|
||||
reinterpret_cast<const uptr>(func_name) } \
|
||||
}
|
||||
|
||||
// For a function foo() and a wrapper function bar() create a global pair
|
||||
// of pointers { bar, foo } in the __DATA,__interpose section.
|
||||
// As a result all the calls to foo() will be routed to bar() at runtime.
|
||||
#define INTERPOSER_2(func_name, wrapper_name) __attribute__((used)) \
|
||||
const interpose_substitution substitution_##func_name[] \
|
||||
__attribute__((section("__DATA, __interpose"))) = { \
|
||||
{ reinterpret_cast<const uptr>(wrapper_name), \
|
||||
reinterpret_cast<const uptr>(func_name) } \
|
||||
}
|
||||
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) "wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...)
|
||||
|
||||
#elif defined(_WIN32)
|
||||
# if defined(_DLL) // DLL CRT
|
||||
# define WRAP(x) x
|
||||
|
@ -144,12 +175,24 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
# define DEFINE_REAL(ret_type, func, ...)
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
#define INTERCEPTOR(ret_type, func, ...) \
|
||||
DEFINE_REAL(ret_type, func, __VA_ARGS__) \
|
||||
DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
|
||||
extern "C" \
|
||||
INTERCEPTOR_ATTRIBUTE \
|
||||
ret_type WRAP(func)(__VA_ARGS__)
|
||||
#else // __APPLE__
|
||||
#define INTERCEPTOR(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__); \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__); \
|
||||
INTERPOSER(func); \
|
||||
extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__)
|
||||
|
||||
// Override |overridee| with |overrider|.
|
||||
#define OVERRIDE_FUNCTION(overridee, overrider) \
|
||||
INTERPOSER_2(overridee, WRAP(overrider))
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define INTERCEPTOR_WINAPI(ret_type, func, ...) \
|
||||
|
@ -183,8 +226,6 @@ typedef unsigned long uptr; // NOLINT
|
|||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func)
|
||||
#elif defined(__APPLE__)
|
||||
# include "interception_mac.h"
|
||||
# define OVERRIDE_FUNCTION(old_func, new_func) \
|
||||
OVERRIDE_FUNCTION_MAC(old_func, new_func)
|
||||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
|
||||
#else // defined(_WIN32)
|
||||
# include "interception_win.h"
|
||||
|
|
Loading…
Reference in New Issue