Hey I actually tried to make such a thing.
https://github.com/matheusmoreira/liblinux
It provides access to Linux system calls and process start up code that gets all the arguments, environment and auxiliary values. I have several examples of applications written in 100% freestanding C with zero dependencies other than this library:
https://github.com/matheusmoreira/liblinux/tree/master/examp...
It's a bit too low level but I actually planned to make my own ld-liblinux eventually. It currently works with static and dynamic linking. The ld-linux.so is able to link even though nothing else links against glibc. I didn't test dynamic loading though.
I stopped working on it because I found better solution for system calls on the Linux kernel repository:
https://github.com/torvalds/linux/blob/master/tools/include/...
Do you think liblinux could have a future?
Yeah, I have seen that file. Thought last I checked, it crashes in certain conditions [1]; I didn't really go back to it to fix it.
> I actually planned to make my own ld-liblinux eventually
Can you share this code? If not, could you document how exactly would you have done this? I lost all motivation once I realized that ld-linux and Glibc communicate with private APIs, but I'd love to actually work on it if it was possible to do this.
> Do you think liblinux could have a future?
Absolutely. The problem with Linux currently is that the loader rejects programs at the sign of slightest incompatibility before even trying to execute them, which means that there is nothing that the application code could do to work around said incompatibility. If we were able to get our code running (or, as I like to say it, make %RIP point inside the main function), we could then do whatever hacky-bullshit was required make the software work. All I need is to get our code executing, and from that point on, I'll handle system compatibility.
_start:
xorq %rbp,%rbp /* user code should mark the deepest stack frame
* by setting the frame pointer to zero
*/
movq %rsp,%rdi
call liblinux_start
movq %rax,%rdi
movq $__NR_exit,%rax
syscall
static void *after(void *vector) {
void **pointer = (void **) vector;
while (*pointer++ != 0);
return pointer;
}
int liblinux_start(void *stack_pointer) {
long count;
char *arguments;
char *environment;
struct auxiliary *values;
count = *((long *) stack_pointer);
arguments = ((char **) stack_pointer) + 1;
environment = arguments + count + 1;
values = after(environment);
/* start is the main function */
return start(count, arguments, environment, values);
}
> Can you share this code? If not, could you document how exactly would you have done this?Unfortunately I didn't make it that far. I was planning to study how glibc and musl do it just like how I studied their system call implementations. If I start working on this again I'm gonna look this up. I'll need to learn this linking stuff in order to support the kernel vDSO anyway.
https://github.com/jart/cosmopolitan/blob/d7ac16a9ed56ebdc70...
I was working on something similar (though much smaller in scope), but had to stop when I realised that `ld-linux.so` has some internal APIs that Glibc uses to setup dlopen()/dlsym(); essentially meaning that it is very hard -if not impossible - to load any shared libraries if one does not link to Glibc.
What I wouldn't give for a liblinux, similar to NTDLL.