Hi Ammar,
On Thu, Dec 22, 2022 at 08:46:15PM +0700, Ammar Faizi wrote:
I agree with following the @envp pointer to get the auxv. I was trying to wire up a new function '__start' (with double underscores) written in C that accepts @argc, @argv and @envp. Then it calls 'main'. Then we call '__start' instead of 'main' from '_start'. This way, we can arrange nolibc-defined data without touching Assembly much in '__start' (before main).
But then I noticed that it wouldn't work because we may have users who define the 'main' function differently, e.g.:
int main(void); int main(int argc, char **argv); int main(int argc, char **argv, char **envp);
So '__start' can't call main. We still need to call the main from the inline Assembly (from '_start').
Yes, and quite frankly I prefer to make that the least complicated. Doing just a simple loop in the _start code is trivial. The main concern was to store the data. Till now we had an optional .bss section, we didn't save environ and errno was optional. But let's be honest, while it does allow for writing the smallest programs, most programs will have at least one global variable and will get this section anyway, so we don't save anything in practice. This concern used to be valid when I was making tiny executables when running on floppies where each byte mattered, but now that's pointless.
Thus what I'm proposing is to switch to weak symbol definitions for errno, environ, and auxv. I did a quick test to make sure that the same symbol was properly used when accessed from two units and that's OK, I'm seeing the same instance for all of them (which is better than the current situation where errno is static, hence per-unit).
My quick-and-dirty test looks like this:
diff --git a/arch-x86_64.h b/arch-x86_64.h index e780fdf..73f7b5f 100644 --- a/arch-x86_64.h +++ b/arch-x86_64.h @@ -209,6 +209,9 @@ struct sys_stat_struct { _ret; \ })
+char **environ __attribute__((weak,unused)); +long *auxv __attribute__((weak,unused)); + /* startup code */ /* * x86-64 System V ABI mandates: @@ -218,11 +221,17 @@ struct sys_stat_struct { */ asm(".section .text\n" ".weak _start\n" "_start:\n" "pop %rdi\n" // argc (first arg, %rdi) "mov %rsp, %rsi\n" // argv[] (second arg, %rsi) "lea 8(%rsi,%rdi,8),%rdx\n" // then a NULL then envp (third arg, %rdx) + "mov %rdx, environ\n" // save environ "xor %ebp, %ebp\n" // zero the stack frame + "mov %rdx, %rax\n" // search for auxv (follows NULL after last en> + "0: add $8, %rax\n" + " cmp -8(%rax), %rbp\n" + " jnz 0b\n" + "mov %rax, auxv\n" // save auxv "and $-16, %rsp\n" // x86 ABI : esp must be 16-byte aligned befor> "call main\n" // main() returns the status code, we'll exit > "mov %eax, %edi\n" // retrieve exit code (32 bit)
diff --git a/errno.h b/errno.h index df0e473..9781077 100644 --- a/errno.h +++ b/errno.h @@ -29,7 +29,8 @@ #include <asm/errno.h>
/* this way it will be removed if unused */ -static int errno; +//static int errno; +int errno __attribute__((weak));
#ifndef NOLIBC_IGNORE_ERRNO #define SET_ERRNO(v) do { errno = (v); } while (0)
$ cat a.c #include "nolibc.h"
extern void b(void);
int main(int argc, char **argv, char **envp) { //environ = envp; errno = 1234; printf("main(): errno=%d env(TERM)=%s auxv=%p auxv[0].t=0x%lx auxv[0].v=0x%lx\n", errno, getenv("TERM"), auxv, auxv?auxv[0]:0, auxv?auxv[1]:0); b(); return 0; }
$ cat b.c #include "nolibc.h"
void b(void) { long *v = auxv;
printf("b(): errno=%d env(TERM)=%s auxv=%p auxv[0].t=0x%lx auxv[0].v=0x%lx\n", errno, getenv("TERM"), auxv, auxv?auxv[0]:0, auxv?auxv[1]:0);
printf("auxv:\n"); while (v && v[0]) { printf(" 0x%lx: 0x%lx\n", v[0], v[1]); v += 2; } }
$ gcc -Os -fno-asynchronous-unwind-tables -include /g/public/nolibc/nolibc.h -Wall -nostdlib -static -o ab a.c b.c
$ nm --size ab 0000000000000004 V errno 0000000000000008 V auxv 0000000000000008 V environ 0000000000000014 W memset 0000000000000018 W memcpy 0000000000000018 W raise 000000000000001b W abort 0000000000000030 W memmove 0000000000000053 t u64toa_r 0000000000000053 t u64toa_r 0000000000000082 T main 00000000000000a4 T b 0000000000000289 t printf 000000000000028c t printf.constprop.0
$ ./ab main(): errno=1234 env(TERM)=xterm auxv=0x7ffdd0c31df8 auxv[0].t=0x21 auxv[0].v=0x7ffdd0d56000 b(): errno=1234 env(TERM)=xterm auxv=0x7ffdd0c31df8 auxv[0].t=0x21 auxv[0].v=0x7ffdd0d56000 auxv: 0x21: 0x7ffdd0d56000 0x10: 0xbfebfbff 0x6: 0x1000 0x11: 0x64 0x3: 0x400040 0x4: 0x38 0x5: 0x7 0x7: 0x0 0x8: 0x0 0x9: 0x401082 0xb: 0x1fd 0xc: 0x1fd 0xd: 0x64 0xe: 0x64 0x17: 0x0 0x19: 0x7ffdd0c31f39 0x1a: 0x2 0x1f: 0x7ffdd0c33ff3 0xf: 0x7ffdd0c31f49
Note that I could verify that some of the entries above are valid (e.g. "x86_64" in 0xf = AT_PLATFORM).
Thus now my focus will be on storing these variables where relevant for all archs, so that your getauxval() implementation works on top of it. It will be much cleaner and will also improve programs' ease of implementation and reliability.
Cheers, Willy
PS: maybe we should trim the Cc list for future exchanges.