mirror of
https://github.com/matrix-construct/construct
synced 2024-05-20 03:43:47 +02:00
ircd::ctx::posix: Trapdoor complex allowing real pthreads to work again.
This commit is contained in:
parent
e37a775e9b
commit
446f319391
|
@ -14,8 +14,7 @@ namespace ircd::ctx::posix
|
||||||
struct disable_pthread;
|
struct disable_pthread;
|
||||||
|
|
||||||
extern log::log log;
|
extern log::log log;
|
||||||
extern bool hook_pthread_create;
|
extern int enable_hook; // -1 = pthread; 0 = auto; 1 = ircd::ctx.
|
||||||
extern bool unhook_pthread_create;
|
|
||||||
extern std::vector<context> ctxs;
|
extern std::vector<context> ctxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,19 +24,19 @@ namespace ircd::ctx::posix
|
||||||
/// disable_pthread.
|
/// disable_pthread.
|
||||||
struct ircd::ctx::posix::enable_pthread
|
struct ircd::ctx::posix::enable_pthread
|
||||||
{
|
{
|
||||||
bool theirs
|
int theirs
|
||||||
{
|
{
|
||||||
unhook_pthread_create
|
enable_hook
|
||||||
};
|
};
|
||||||
|
|
||||||
enable_pthread(const bool &ours = true)
|
enable_pthread(const bool &ours = true)
|
||||||
{
|
{
|
||||||
unhook_pthread_create = ours;
|
enable_hook = ours? -1: theirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
~enable_pthread() noexcept
|
~enable_pthread() noexcept
|
||||||
{
|
{
|
||||||
unhook_pthread_create = theirs;
|
enable_hook = theirs;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,18 +47,18 @@ struct ircd::ctx::posix::enable_pthread
|
||||||
/// precedence.
|
/// precedence.
|
||||||
struct ircd::ctx::posix::disable_pthread
|
struct ircd::ctx::posix::disable_pthread
|
||||||
{
|
{
|
||||||
bool theirs
|
int theirs
|
||||||
{
|
{
|
||||||
hook_pthread_create
|
enable_hook
|
||||||
};
|
};
|
||||||
|
|
||||||
disable_pthread(const bool &ours = true)
|
disable_pthread(const bool &ours = true)
|
||||||
{
|
{
|
||||||
hook_pthread_create = ours;
|
enable_hook = ours? 1: theirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
~disable_pthread()
|
~disable_pthread() noexcept
|
||||||
{
|
{
|
||||||
hook_pthread_create = theirs;
|
enable_hook = theirs;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,9 @@ AM_LDFLAGS = \
|
||||||
-Wl,--unresolved-symbols=ignore-in-shared-libs \
|
-Wl,--unresolved-symbols=ignore-in-shared-libs \
|
||||||
-Wl,--wrap=pthread_create \
|
-Wl,--wrap=pthread_create \
|
||||||
-Wl,--wrap=pthread_join \
|
-Wl,--wrap=pthread_join \
|
||||||
|
-Wl,--wrap=pthread_tryjoin_np \
|
||||||
-Wl,--wrap=pthread_timedjoin_np \
|
-Wl,--wrap=pthread_timedjoin_np \
|
||||||
|
-Wl,--wrap=pthread_clockjoin_np \
|
||||||
-Wl,--wrap=pthread_self \
|
-Wl,--wrap=pthread_self \
|
||||||
-Wl,--wrap=pthread_setname_np \
|
-Wl,--wrap=pthread_setname_np \
|
||||||
-Wl,-z,nodelete \
|
-Wl,-z,nodelete \
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
// the project and to other users of the address space.
|
// the project and to other users of the address space.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "ctx_posix.h"
|
#include "ctx_posix.h"
|
||||||
|
|
||||||
|
@ -23,35 +24,50 @@
|
||||||
|
|
||||||
namespace ircd::ctx::posix
|
namespace ircd::ctx::posix
|
||||||
{
|
{
|
||||||
|
static bool is_main_thread() noexcept;
|
||||||
|
static bool hook_enabled() noexcept;
|
||||||
static bool is(const pthread_t &) noexcept;
|
static bool is(const pthread_t &) noexcept;
|
||||||
|
|
||||||
|
[[gnu::visibility("internal")]]
|
||||||
|
extern void *real_pthread; // custom_ptr<void> real_pthread;
|
||||||
}
|
}
|
||||||
|
|
||||||
using ircd::always_assert;
|
using ircd::always_assert;
|
||||||
|
|
||||||
|
/// Unit's logging facility.
|
||||||
decltype(ircd::ctx::posix::log)
|
decltype(ircd::ctx::posix::log)
|
||||||
ircd::ctx::posix::log
|
ircd::ctx::posix::log
|
||||||
{
|
{
|
||||||
"ctx.posix"
|
"ctx.posix"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// When asserted true, real pthread is never created and ircd::ctx is
|
/// Points to a dlopen(3) handle of libpthread.so to give us the untainted
|
||||||
/// spawned instead. By default it's false, which will allow other settings
|
/// location of real pthread functions; regardless of our hook solution for
|
||||||
/// or automated determination to decide.
|
/// the platform. It remains null until this interface is used to spawn an
|
||||||
decltype(ircd::ctx::posix::hook_pthread_create)
|
/// actually real pthread. Note that this might not be available if static
|
||||||
ircd::ctx::posix::hook_pthread_create;
|
/// destruction occurs prior to any joining thread accessing it.
|
||||||
|
decltype(ircd::ctx::posix::real_pthread)
|
||||||
|
ircd::ctx::posix::real_pthread
|
||||||
|
{
|
||||||
|
nullptr, //::dlclose
|
||||||
|
};
|
||||||
|
|
||||||
/// When asserted true, real pthread is always created and ircd::ctx is
|
/// -1 = pthread interface not hooked, forwards to real pthread.
|
||||||
/// never spawned. By default it's false, which will allow other settings
|
/// 0 = determined automatically based on contextual information.
|
||||||
/// or automated determination to decide.
|
/// 1 = pthread interface hooked, forwards to ircd::ctx.
|
||||||
decltype(ircd::ctx::posix::unhook_pthread_create)
|
decltype(ircd::ctx::posix::enable_hook)
|
||||||
ircd::ctx::posix::unhook_pthread_create;
|
ircd::ctx::posix::enable_hook;
|
||||||
|
|
||||||
|
/// State container for ircd::ctx's that are being operated through the hooked
|
||||||
|
/// pthread interface.
|
||||||
decltype(ircd::ctx::posix::ctxs)
|
decltype(ircd::ctx::posix::ctxs)
|
||||||
ircd::ctx::posix::ctxs;
|
ircd::ctx::posix::ctxs;
|
||||||
|
|
||||||
|
/// Hook generation macro. If the hook is accomplished with __wrap/__real
|
||||||
|
/// then also be sure to add the line to the linker flags in Makefile.am.
|
||||||
#define IRCD_WRAP(symbol, target, prototype, body) \
|
#define IRCD_WRAP(symbol, target, prototype, body) \
|
||||||
extern "C" int __real_##symbol prototype; \
|
extern "C" int __real_##symbol prototype; \
|
||||||
extern "C" int __wrap_##symbol prototype body \
|
extern "C" int __wrap_##symbol prototype noexcept body \
|
||||||
extern "C" int symbol prototype __attribute__((weak, alias(target)));
|
extern "C" int symbol prototype __attribute__((weak, alias(target)));
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -65,22 +81,39 @@ IRCD_WRAP(pthread_create, "__wrap_pthread_create",
|
||||||
const pthread_attr_t *const attr,
|
const pthread_attr_t *const attr,
|
||||||
void *(*const start_routine)(void *),
|
void *(*const start_routine)(void *),
|
||||||
void *const arg
|
void *const arg
|
||||||
), {
|
),
|
||||||
const bool hook_enabled
|
{
|
||||||
{
|
if(ircd::ctx::posix::hook_enabled())
|
||||||
// When enable_pthread is asserted, the hook is never enabled.
|
return ircd_pthread_create(thread, attr, start_routine, arg);
|
||||||
!ircd::ctx::posix::unhook_pthread_create
|
|
||||||
|
|
||||||
// When disable_pthread is asserted, the hook is otherwise enabled.
|
// hack on the hack: linker's __real_pthread_create isn't working and we
|
||||||
// Otherwise if we're already running on an ircd::ctx stack we continue
|
// need something stronger.
|
||||||
// to spawn more ircd::ctx. By default on another stack (or main stack)
|
if(!ircd::ctx::posix::real_pthread)
|
||||||
// we spawn a true pthread.
|
{
|
||||||
&& (ircd::ctx::posix::hook_pthread_create || ircd::ctx::current)
|
// 2p the dlopen() handle so it's init once by any real thread.
|
||||||
|
static std::mutex mutex;
|
||||||
|
const std::lock_guard lock
|
||||||
|
{
|
||||||
|
mutex
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!ircd::ctx::posix::real_pthread)
|
||||||
|
ircd::ctx::posix::real_pthread = //.reset
|
||||||
|
(
|
||||||
|
dlopen("libpthread.so", RTLD_LOCAL | RTLD_LAZY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ircd::ctx::posix::real_pthread);
|
||||||
|
const auto __real_pthread_create
|
||||||
|
{
|
||||||
|
reinterpret_cast<int (*)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *)>
|
||||||
|
(
|
||||||
|
dlsym(ircd::ctx::posix::real_pthread, "pthread_create")
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
return hook_enabled?
|
return __real_pthread_create(thread, attr, start_routine, arg);
|
||||||
ircd_pthread_create(thread, attr, start_routine, arg):
|
|
||||||
__real_pthread_create(thread, attr, start_routine, arg);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -120,10 +153,22 @@ IRCD_WRAP(pthread_join, "__wrap_pthread_join",
|
||||||
(
|
(
|
||||||
pthread_t __th,
|
pthread_t __th,
|
||||||
void **__thread_return
|
void **__thread_return
|
||||||
), {
|
),
|
||||||
return ircd::ctx::posix::is(__th)?
|
{
|
||||||
ircd_pthread_join(__th, __thread_return):
|
if(ircd::ctx::posix::is(__th))
|
||||||
__real_pthread_join(__th, __thread_return);
|
return ircd_pthread_join(__th, __thread_return);
|
||||||
|
|
||||||
|
assert(ircd::ctx::posix::real_pthread);
|
||||||
|
const auto __real_pthread_join
|
||||||
|
{
|
||||||
|
reinterpret_cast<int (*)(pthread_t, void **)>
|
||||||
|
(
|
||||||
|
dlsym(ircd::ctx::posix::real_pthread, "pthread_join")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(__real_pthread_join);
|
||||||
|
return __real_pthread_join(__th, __thread_return);
|
||||||
})
|
})
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -156,6 +201,27 @@ noexcept
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IRCD_WRAP(pthread_tryjoin_np, "__wrap_pthread_tryjoin_np",
|
||||||
|
(
|
||||||
|
pthread_t __th,
|
||||||
|
void **__thread_return
|
||||||
|
),
|
||||||
|
{
|
||||||
|
if(ircd::ctx::posix::is(__th))
|
||||||
|
return ircd_pthread_tryjoin_np(__th, __thread_return);
|
||||||
|
|
||||||
|
assert(ircd::ctx::posix::real_pthread);
|
||||||
|
const auto __real_pthread_tryjoin_np
|
||||||
|
{
|
||||||
|
reinterpret_cast<int (*)(pthread_t, void **)>
|
||||||
|
(
|
||||||
|
dlsym(ircd::ctx::posix::real_pthread, "pthread_tryjoin_np")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
return __real_pthread_tryjoin_np(__th, __thread_return);
|
||||||
|
})
|
||||||
|
|
||||||
int
|
int
|
||||||
ircd_pthread_tryjoin_np(pthread_t __th,
|
ircd_pthread_tryjoin_np(pthread_t __th,
|
||||||
void **__thread_return)
|
void **__thread_return)
|
||||||
|
@ -170,11 +236,23 @@ IRCD_WRAP(pthread_timedjoin_np, "__wrap_pthread_timedjoin_np",
|
||||||
pthread_t __th,
|
pthread_t __th,
|
||||||
void **__thread_return,
|
void **__thread_return,
|
||||||
const struct timespec *__abstime
|
const struct timespec *__abstime
|
||||||
), {
|
),
|
||||||
return ircd::ctx::posix::is(__th)?
|
{
|
||||||
ircd_pthread_timedjoin_np(__th, __thread_return, __abstime):
|
if(ircd::ctx::posix::is(__th))
|
||||||
__real_pthread_timedjoin_np(__th, __thread_return, __abstime);
|
return ircd_pthread_timedjoin_np(__th, __thread_return, __abstime);
|
||||||
});
|
|
||||||
|
assert(ircd::ctx::posix::real_pthread);
|
||||||
|
const auto __real_pthread_timedjoin_np
|
||||||
|
{
|
||||||
|
reinterpret_cast<int (*)(pthread_t, void **, const struct timespec *)>
|
||||||
|
(
|
||||||
|
dlsym(ircd::ctx::posix::real_pthread, "pthread_timedjoin_np")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(__real_pthread_timedjoin_np);
|
||||||
|
return __real_pthread_timedjoin_np(__th, __thread_return, __abstime);
|
||||||
|
})
|
||||||
|
|
||||||
int
|
int
|
||||||
ircd_pthread_timedjoin_np(pthread_t __th,
|
ircd_pthread_timedjoin_np(pthread_t __th,
|
||||||
|
@ -187,6 +265,42 @@ noexcept
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IRCD_WRAP(pthread_clockjoin_np, "__wrap_pthread_clockjoin_np",
|
||||||
|
(
|
||||||
|
pthread_t __th,
|
||||||
|
void **__thread_return,
|
||||||
|
clockid_t clockid,
|
||||||
|
const struct timespec *__abstime
|
||||||
|
),
|
||||||
|
{
|
||||||
|
if(ircd::ctx::posix::is(__th))
|
||||||
|
return ircd_pthread_clockjoin_np(__th, __thread_return, clockid, __abstime);
|
||||||
|
|
||||||
|
assert(ircd::ctx::posix::real_pthread);
|
||||||
|
const auto __real_pthread_clockjoin_np
|
||||||
|
{
|
||||||
|
reinterpret_cast<int (*)(pthread_t, void **, clockid_t, const struct timespec *)>
|
||||||
|
(
|
||||||
|
dlsym(ircd::ctx::posix::real_pthread, "pthread_clockjoin_np")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(__real_pthread_clockjoin_np);
|
||||||
|
return __real_pthread_clockjoin_np(__th, __thread_return, clockid, __abstime);
|
||||||
|
})
|
||||||
|
|
||||||
|
int
|
||||||
|
ircd_pthread_clockjoin_np(pthread_t __th,
|
||||||
|
void **__thread_return,
|
||||||
|
clockid_t clockid,
|
||||||
|
const struct timespec *__abstime)
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
//TODO: XXX ctx clock join
|
||||||
|
ircd_pthread_join(__th, __thread_return);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
__attribute__((noreturn))
|
__attribute__((noreturn))
|
||||||
ircd_pthread_exit(void *const retval)
|
ircd_pthread_exit(void *const retval)
|
||||||
|
@ -210,9 +324,17 @@ __real_pthread_self(void);
|
||||||
extern "C" pthread_t
|
extern "C" pthread_t
|
||||||
__wrap_pthread_self(void)
|
__wrap_pthread_self(void)
|
||||||
{
|
{
|
||||||
return ircd::ctx::current?
|
const bool hook_enabled
|
||||||
ircd_pthread_self():
|
{
|
||||||
__real_pthread_self();
|
true
|
||||||
|
&& ircd::ctx::current
|
||||||
|
&& ircd::ctx::posix::enable_hook >= 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if(hook_enabled)
|
||||||
|
return ircd_pthread_self();
|
||||||
|
|
||||||
|
return __real_pthread_self();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -360,10 +482,22 @@ IRCD_WRAP(pthread_setname_np, "__wrap_pthread_setname_np",
|
||||||
(
|
(
|
||||||
pthread_t __target_thread,
|
pthread_t __target_thread,
|
||||||
const char *__name
|
const char *__name
|
||||||
), {
|
),
|
||||||
return ircd::ctx::posix::is(__target_thread)?
|
{
|
||||||
ircd_pthread_setname_np(__target_thread, __name):
|
if(ircd::ctx::posix::is(__target_thread))
|
||||||
__real_pthread_setname_np(__target_thread, __name);
|
return ircd_pthread_setname_np(__target_thread, __name);
|
||||||
|
|
||||||
|
assert(ircd::ctx::posix::real_pthread);
|
||||||
|
const auto __real_pthread_setname_np
|
||||||
|
{
|
||||||
|
reinterpret_cast<int (*)(pthread_t, const char *)>
|
||||||
|
(
|
||||||
|
dlsym(ircd::ctx::posix::real_pthread, "pthread_setname_np")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(__real_pthread_setname_np);
|
||||||
|
return __real_pthread_setname_np(__target_thread, __name);
|
||||||
})
|
})
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1532,6 +1666,10 @@ bool
|
||||||
ircd::ctx::posix::is(const pthread_t &target)
|
ircd::ctx::posix::is(const pthread_t &target)
|
||||||
noexcept
|
noexcept
|
||||||
{
|
{
|
||||||
|
// Can't be an ircd::ctx if it's not the main thread, nor can we look.
|
||||||
|
if(!is_main_thread())
|
||||||
|
return false;
|
||||||
|
|
||||||
const auto it
|
const auto it
|
||||||
{
|
{
|
||||||
std::find_if(begin(ctxs), end(ctxs), [&]
|
std::find_if(begin(ctxs), end(ctxs), [&]
|
||||||
|
@ -1543,3 +1681,39 @@ noexcept
|
||||||
|
|
||||||
return it != end(ctxs);
|
return it != end(ctxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::ctx::posix::hook_enabled()
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
// The hook is only enabled on the main thread.
|
||||||
|
if(!is_main_thread())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// When disable_pthread is asserted, the hook is always enabled.
|
||||||
|
if(ircd::ctx::posix::enable_hook > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// When enable_pthread is asserted, the hook is never enabled.
|
||||||
|
if(ircd::ctx::posix::enable_hook < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Consider the hook enabled if called from an ircd::ctx stack, since
|
||||||
|
// that is clearly our code, and if we call into a library on such a
|
||||||
|
// stack we will use an explicit enable_pthread if we need it.
|
||||||
|
//
|
||||||
|
// OTOH, when not on an ircd::ctx stack, we assume the call is coming from
|
||||||
|
// some other code is running somewhere else in the address space, perhaps
|
||||||
|
// totally unrelated, and we give that the expected unhooked behavior.
|
||||||
|
return ircd::ctx::current != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::ctx::posix::is_main_thread()
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
|| ircd::ios::handler::current != nullptr
|
||||||
|
|| ircd::ctx::current != nullptr
|
||||||
|
|| ircd::ctx::is_main_thread();
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,13 @@ ircd_pthread_timedjoin_np(pthread_t __th,
|
||||||
const struct timespec *__abstime)
|
const struct timespec *__abstime)
|
||||||
noexcept;
|
noexcept;
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
ircd_pthread_clockjoin_np(pthread_t __th,
|
||||||
|
void **__thread_return,
|
||||||
|
clockid_t clockid,
|
||||||
|
const struct timespec *__abstime)
|
||||||
|
noexcept;
|
||||||
|
|
||||||
extern "C" int
|
extern "C" int
|
||||||
ircd_pthread_detach(pthread_t __th)
|
ircd_pthread_detach(pthread_t __th)
|
||||||
noexcept;
|
noexcept;
|
||||||
|
|
|
@ -17,7 +17,10 @@ ircd::ios::log
|
||||||
|
|
||||||
/// "main" thread for IRCd; the one the main context landed on.
|
/// "main" thread for IRCd; the one the main context landed on.
|
||||||
decltype(ircd::ios::main_thread_id)
|
decltype(ircd::ios::main_thread_id)
|
||||||
ircd::ios::main_thread_id;
|
ircd::ios::main_thread_id
|
||||||
|
{
|
||||||
|
std::this_thread::get_id()
|
||||||
|
};
|
||||||
|
|
||||||
/// The embedder/executable's (library user) asio::executor provided on init.
|
/// The embedder/executable's (library user) asio::executor provided on init.
|
||||||
decltype(ircd::ios::user)
|
decltype(ircd::ios::user)
|
||||||
|
|
Loading…
Reference in a new issue