jit: 1.003483 ns/call
plt: 1.254158 ns/call
ind: 1.254616 ns/call
GCC does not seem to support it though, even if it accepts the flag and gives me: jit: 1.003483 ns/call
plt: 1.502089 ns/call
ind: 1.254616 ns/call
(tried everything I could think of that would have a chance to make the PLT disappear: cc -fno-plt -Bsymbolic -fno-semantic-interposition -flto -std=c99 -Wall -Wextra -O3 -g3 -Wl,-z,relro,-z,now -o benchmark benchmark.c ./empty.so -ldl
without any change on GCC)In the early Python 2 era there was an option to build an interpreter binary with statically linked C stubs, and it was noticeably faster and let you access Python data structures from C. I used it for robotics code for speed. It was inconvenient because you had to link in all the modules you needed.
Ocaml's C-implemented functions are linked statically. But like JNI, the C functions have special names and type signatures, so it is slightly different from, say, ctypes in Python.
CGO for Go is statically linked, too. Its overhead stems from significant differences between the Go and C world. The example uses dynamic linking, but it would not have to do that.
Yes it's just called linking. The language needs to be aware of calling conventions and perhaps side effects and be prepared for no additional intrinsic support for higher level features.
It probably also needs to be able to read C headers, because C symbols do not contain type signatures like many C++ compilers add.
There's no "library" or some out of the box solution for this, if that's what you're asking. This boils down to how programs are constructed and, moreso, how CPUs work.
In most (all?) cases, anything higher level than straight-up linking is headed toward FFI territory.