I believe the process always holds the binary open as a "txt" file descriptor (check lsof), so opening `/proc/self/exe` always works.
You can even execve a "memfd", an in-memory "file" which is not in the filesystem (distinct from a ramdisk file, which is a file sitting on an in-memory filesystem). /proc/self/exe still works even in that case, even when the original memfd is closed.
Note that argv[0] can never be relied on 100%. The vast majority of programs will set it correctly (especially since many programs will malfunction if provided bogus argv[0]), but a caller has full control over argv[0] and can set it to anything, including a NULL pointer (by simply passing an empty argv array).
> I believe the process always holds the binary open as a "txt" file descriptor
To be clear, the kernel has an association between the process and the "txt" file (because it is mmaped in), but this is not an application file descriptor (like 0, 1, 2, ...). If an application wants to read from it, and it isn't already mapped by a LOAD section, it needs to open() a real file descriptor.
Absolutely. It's certainly not mounted automatically after Linux boots and depending on the system's configuration it might never get mounted at all. Maybe it could even use some other path.
One of my long term goals with the programming language I posted is to boot Linux directly into the interpreter and bring up the entire system from inside it. Not only will /proc not be mounted, my program's gonna be the one that mounts it. So I decided to avoid using tricks like reading /proc/self/exe.
You can even execve a "memfd", an in-memory "file" which is not in the filesystem (distinct from a ramdisk file, which is a file sitting on an in-memory filesystem). /proc/self/exe still works even in that case, even when the original memfd is closed.
Note that argv[0] can never be relied on 100%. The vast majority of programs will set it correctly (especially since many programs will malfunction if provided bogus argv[0]), but a caller has full control over argv[0] and can set it to anything, including a NULL pointer (by simply passing an empty argv array).