You want it transitive in all cases. When a different binary is executed, that’s not about transitivity anymore, as the process memory will be completely replaced and the linking procedure starts over again. That’s why all binaries need to be replaced by wrappers, not just the commands referenced by applications.
About patching the interpreter, there’s no reason to patch the kernel for that:
/tmp $ cat myld.go
package main
func main() {
println("Hello there!")
}
/tmp $ CGO_ENABLED=0 go build myld.go
/tmp $ patchelf --set-interpreter /tmp/myld teleconsole
/tmp $ ./teleconsole
Hello there!
This is probably the right way to go. We can chain load the real ld-linux here:
/tmp $ cat myld.go
package main
import (
"os"
"syscall"
)
func main() {
const ld = "/lib64/ld-linux-x86-64.so.2"
err := syscall.Exec(ld, []string{ld, "--list", "--library-path", "/snap/core/current/lib/x86_64-linux-gnu", "/tmp/teleconsole"}, os.Environ())
if err != nil {
println("error: " + err.Error())
os.Exit(1)
}
}
Note I used –list above to demonstrate the idea below.
Then, using the teleconsole with the patched interpreter.
/tmp $ ./teleconsole
linux-vdso.so.1 => (0x00007ffdac6db000)
libpthread.so.0 => /snap/core/current/lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f082ed49000)
libc.so.6 => /snap/core/current/lib/x86_64-linux-gnu/libc.so.6 (0x00007f082e97f000)
/tmp/myld => /lib64/ld-linux-x86-64.so.2 (0x000055e1bccb3000)
This has the disadvantage that we patch all binaries, but it feels like a more polished approach, without any wrappers dangling around, no moves, and executions based on argv[0] should still work correctly.
We’ll need some data inside the ELF so we can tell what the original interpreter was. Or perhaps an individual ld for every interpreter required. Those can be tiny little programs (not Go) so perhaps simpler and not unreasonable.
That may also be better than just patching the ELF’s RPATH, because although RPATH would be simpler, the ELF would still point to an ld-linux interpreter outside the snap which will be different and may not even exist depending on local naming conventions adopted by the Linux distribution at hand.
Gut feeling is that this is two or three days of work… a week at most. What do you think?