C++ Logo

std-proposals

Advanced search

[std-proposals] interceptor functions (tested and working on x86_64)

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Mon, 27 Apr 2026 22:42:48 +0100
Consider intercepting a call to a function such as 'printf'. Instead
of parsing all the percentage signs individually, you just want to
forward all the arguments on. So the interceptor should be totally
agnostic to the signature of the target function -- totally oblivious
to its return type and parameter types. Like this:

#include <cstdio> // printf
#include <chrono> // seconds
#include <thread> // sleep_for

[[interceptor]] void MyInterceptor(void) noexcept
{
  // Sleep for 1 second before jumping to 'printf'

  std::this_thread::sleep_for( std::chrono::seconds(1) );
  goto -> std::printf;
}

int main(int const argc, char **const argv)
{
  auto const fp = (decltype(std::printf)*)&MyInterceptor;

  fp("My favourite number is %i,\nand my favourite word is %s.\n",
     argc,
     argv[ argc - 1 ]);
}

And well here it is now working up on GodBolt:

    https://godbolt.org/z/6qqTrq6Wj

I want to explain to compiler vendors just how easy it is to implement
this new feature. Here's what you do:

Step 1 - When the compile encounters a function with the attribute
[[interceptor]], create a second function declaration with the same
name but prepended with "__core_", and change the second declaration's
return type from 'void' to 'void(*)(void)'. Compile the body of the
function into the second declaration. (The original declaration will
become a thunk).

Step 2 - Inside the body of the interceptor function, the 'return'
statement should instead be written as 'goto ->'.

Step 3 - Emit a thunk with the name of the original function
declaration and give it assembler as follows:

    push_all_arguments
    call __core_Func
    pop_all_arguments
    jmp target_function

It really is that simple. And because the original function name is
mangled before "__core_" is prepended, it also works with templates:

    https://godbolt.org/z/8bYja9zfn

In the above GodBolt, the original function name is mangled as
"_Z13MyInterceptorILi0EEvv", and so then the core becomes
"__core__Z13MyInterceptorILi0EEvv". You can have multiple template
instantiations:

    https://godbolt.org/z/exxz5vcGo

Easy peasy. Here's what I've got so far for the GNU compiler patch:

    https://github.com/healytpk/gcc-thomas-healy/commit/tag_interceptor

It's not perfect but it's a really good proof of concept.

Note that for any given target CPU instruction set, such as x86_64,
the interceptor will be able to intercept all calling conventions and
all CPU configurations. So for example on x86_64, it can intercept
both System V and MSabi, and also it can intercept SSE processors as
well as AVX-512 processors (it checks at runtime to see what the CPU
is capable of).

Next I will code it for x86_32, and it will work for stdcall, cdecl,
thiscall and so on. After that will be aarch64, followed by 32-Bit
ARM.

Received on 2026-04-27 21:43:03