This repository contains experimental code for thread-like processes, or multiple programs running in a shared address space. This blends the Posix process model with the pthread programming model.
All Markdown files were written by hand. Claude assisted with some code for this proof-of-concept, particularly around the ELF loading and the aarch64 trampoline.
DO NOT use this for anything beyond a trivial test. Bad things will probably happen.
The server utility "hosts" a virtual address space, and by using launcher to start programs, those programs can coexist in that address space.
Applications can share pointers in the virtual address space through some out-of-band mechanism (Demo uses copy/paste, dummy_server/client uses sockets, libtproc provides server-global scratch space), and then directly dereference those pointers, as they're valid in the shared address space.
The code for the demoed programs is at example/allocstr.cpp and example/printstr.cpp, and neither contains any magic (/proc/[pid]/mem, etc), nor awareness of the server and launcher.
output.mp4
libtproc provides very rudimentary detection of execution as a threadproc, and allows hosted threadprocs to access a "server-global" scratch space.
Applications can build tooling using this space to implement service discovery and bootstrap shared memory-backed IPC.
- Rough proof of concept examples in
test/, aarch64+Linux only! (Developed in VM on M1 Macbook Air) - Architecture agnostic
- Production quality
- Secure
- Documentation
- Tooling for peer/service discovery (basic)
- Safe Rust example
Use Linux on aarch64, other architectures not supported. This was developed in a VM running Debian on a Macbook Air M1.
Dependencies:
apt install build-essential liburing-devNotably there is no dependency no ELF libraries aside from Linux system headers, though those would probably make the code nicer.
Building:
makeRun auto integration tests:
make testOr run your own programs in a shared address space:
./buildout/server /tmp/mytest.sock &
./buildout/launcher /tmp/mytest.sock program1 arg1 arg2 &
./buildout/launcher /tmp/mytest.sock program2 arg3 arg4Read the overview or implementation for information on the project, or read comparisons to existing work. I've also collected some fun lessons learned in conclusions.
The main (known) requirement is that target applications be compiled as "position independent" executables, with all loadable executables also being position independent. This is the default with Debian GCC, and used to support various flavors of ASLR. This also doesn't carry much overhead over fixed-position executables, and the costs of position independed dynamic shared libraries are well understood and easily mitigated with some effort.
Another major restriction is that because brk() and sbrk() are address space-global from a kernel perspective, applications can't reliably use them.
The server sets the MALLOC_MMAP_THRESHOLD_=0 environment variable to change the default glibc behavior to avoid these.
Things will break if you try to use this.
Another restriction is of using mmap with MAP_FIXED in unfriendly ways.
This is always advised against regardless (see the manpage section Using MAP_FIXED safely).
Perhaps a future extension would be to hook mmap and related syscalls in the server process to enforce applications don't clobber each other.
The conventional ~MAP_FIXED use of mmap() won't cause issues.