Date: Mon, 4 May 2026 07:24:56 +0000
I’m thinking of going a step further with this. Up until now, we can
intercept on the way in:
[[intercept]] void Func(void) noexcept
{
DoLogging();
goto -> std::fprintf;
}
But it would also be possible to intercept on the way out, meaning we could
lock and unlock a mutex. Ideally we would be able to do the following:
std::mutex m;
[[intercept]] void Func(void) noexcept
{
std::lock_guard mylock(m);
goto -> std::fprintf;
}
That would be quite complicated to implement though, as under the hood
you’d need to make it do something like:
std::mutex m;
[[intercept]] void Func(void) noexcept
{
::new( some_where ) std::lock_guard(m);
goto -> std::fprintf;
some_where->~lock_guard<std::mutex>();
}
Getting the target function to jump back to the interceptor is the easy
part – you only need to change the return address on the stack (or the link
register on aarch64). I’ve heard rumours of something called ‘Control-flow
Enforcement Technology’ which would prevent this on x86_64, but MS-Windows
can disable it with 'SetProcessMitigationPolicy', or 'prctl' on Linux. The
tricky part is where to put the ‘lock_guard’, as it cannot go on the stack
– remember the stack must remain intact for the jump. I suppose it could go
in a thread_local variable. Realistically I think the first ‘proof of
concept’ implementation would need to look something like the following:
std::mutex m;
[[intercept]] void Func(void) noexcept
{
m.lock();
goto -> std::fprintf
<-- [](void *p){ m.unlock(); return p; };
}
So basically the above snippet would do five things:
(1) lock the mutex
(2) store the original return address somewhere (perhaps in a thread_local
variable)
(3) alter the return address on the stack to the entry point of the lambda
body
(4) jump to the target function
I think I’d be able to implement the above snippet in the GNU compiler
without too much hassle; basically it would work as follows:
goto -> target_address <-- address_of_outward_interceptor
We can argue about the syntax another day, I’m only concerned about getting
it working for now. An alternative syntax could be:
std::jump_to( target_address, address_of_outward_interceptor );
And if the second argument is ‘nullptr’ then there’s no outward
interception.
I would find this extremely useful for debugging. Let's say we suspect that
a library has a race condition or data race that is causing it to
malfunction -- well we can just wrap every function call in a
mutex-locking-and-unlocking to see if that fixes it.
intercept on the way in:
[[intercept]] void Func(void) noexcept
{
DoLogging();
goto -> std::fprintf;
}
But it would also be possible to intercept on the way out, meaning we could
lock and unlock a mutex. Ideally we would be able to do the following:
std::mutex m;
[[intercept]] void Func(void) noexcept
{
std::lock_guard mylock(m);
goto -> std::fprintf;
}
That would be quite complicated to implement though, as under the hood
you’d need to make it do something like:
std::mutex m;
[[intercept]] void Func(void) noexcept
{
::new( some_where ) std::lock_guard(m);
goto -> std::fprintf;
some_where->~lock_guard<std::mutex>();
}
Getting the target function to jump back to the interceptor is the easy
part – you only need to change the return address on the stack (or the link
register on aarch64). I’ve heard rumours of something called ‘Control-flow
Enforcement Technology’ which would prevent this on x86_64, but MS-Windows
can disable it with 'SetProcessMitigationPolicy', or 'prctl' on Linux. The
tricky part is where to put the ‘lock_guard’, as it cannot go on the stack
– remember the stack must remain intact for the jump. I suppose it could go
in a thread_local variable. Realistically I think the first ‘proof of
concept’ implementation would need to look something like the following:
std::mutex m;
[[intercept]] void Func(void) noexcept
{
m.lock();
goto -> std::fprintf
<-- [](void *p){ m.unlock(); return p; };
}
So basically the above snippet would do five things:
(1) lock the mutex
(2) store the original return address somewhere (perhaps in a thread_local
variable)
(3) alter the return address on the stack to the entry point of the lambda
body
(4) jump to the target function
I think I’d be able to implement the above snippet in the GNU compiler
without too much hassle; basically it would work as follows:
goto -> target_address <-- address_of_outward_interceptor
We can argue about the syntax another day, I’m only concerned about getting
it working for now. An alternative syntax could be:
std::jump_to( target_address, address_of_outward_interceptor );
And if the second argument is ‘nullptr’ then there’s no outward
interception.
I would find this extremely useful for debugging. Let's say we suspect that
a library has a race condition or data race that is causing it to
malfunction -- well we can just wrap every function call in a
mutex-locking-and-unlocking to see if that fixes it.
Received on 2026-05-04 07:25:00
