[RFC PATCH 2/9] tools/nolibc: crt: Split _start_c() into stack-only and normal part

Daniel Palmer posted 9 patches 1 week, 1 day ago
There is a newer version of this series
[RFC PATCH 2/9] tools/nolibc: crt: Split _start_c() into stack-only and normal part
Posted by Daniel Palmer 1 week, 1 day ago
To prepare for nolibc programs being able to relocate themselves
we need to split _start_c() into two parts:

- One part that only uses the stack so there are no accesses via
  the GOT etc that isn't setup yet. Note that on m68k at least
  this also means forcing the stackprotector off because accessing
  the stack protector canary is done via the GOT.

- Another part that is called after we have done relocation so it
  is safe to access global variables etc that might use the GOT etc.

Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
 tools/include/nolibc/crt.h | 57 +++++++++++++++++++++++---------------
 1 file changed, 35 insertions(+), 22 deletions(-)

diff --git a/tools/include/nolibc/crt.h b/tools/include/nolibc/crt.h
index d9262998dae9..899062c00fb7 100644
--- a/tools/include/nolibc/crt.h
+++ b/tools/include/nolibc/crt.h
@@ -27,26 +27,51 @@ extern void (*const __init_array_end[])(int, char **, char**) __attribute__((wea
 extern void (*const __fini_array_start[])(void) __attribute__((weak));
 extern void (*const __fini_array_end[])(void) __attribute__((weak));
 
-void _start_c(long *sp);
-__attribute__((weak,used))
+void __start_c(long argc, char **argv, char **envp, const unsigned long *auxv);
+__attribute__((weak, used))
 #if __nolibc_has_feature(undefined_behavior_sanitizer)
 	__attribute__((no_sanitize("function")))
 #endif
-void _start_c(long *sp)
+void __no_stack_protector __start_c(long argc, char **argv, char **envp, const unsigned long *auxv)
 {
-	long argc;
-	char **argv;
-	char **envp;
-	int exitcode;
 	void (* const *ctor_func)(int, char **, char **);
 	void (* const *dtor_func)(void);
-	const unsigned long *auxv;
 	/* silence potential warning: conflicting types for 'main' */
 	int _nolibc_main(int, char **, char **) __asm__ ("main");
+	int exitcode;
 
 	/* initialize stack protector */
 	__stack_chk_init();
 
+	environ = envp;
+	_auxv = auxv;
+
+	for (ctor_func = __preinit_array_start; ctor_func < __preinit_array_end; ctor_func++)
+		(*ctor_func)(argc, argv, envp);
+	for (ctor_func = __init_array_start; ctor_func < __init_array_end; ctor_func++)
+		(*ctor_func)(argc, argv, envp);
+
+	/* go to application */
+	exitcode = _nolibc_main(argc, argv, envp);
+
+	for (dtor_func = __fini_array_end; dtor_func > __fini_array_start;)
+		(*--dtor_func)();
+
+	exit(exitcode);
+}
+
+void _start_c(long *sp);
+__attribute__((weak, used))
+#if __nolibc_has_feature(undefined_behavior_sanitizer)
+	__attribute__((no_sanitize("function")))
+#endif
+void __no_stack_protector _start_c(long *sp)
+{
+	long argc;
+	char **argv;
+	char **envp;
+	const unsigned long *auxv;
+
 	/*
 	 * sp  :    argc          <-- argument count, required by main()
 	 * argv:    argv[0]       <-- argument vector, required by main()
@@ -69,25 +94,13 @@ void _start_c(long *sp)
 	argv = (void *)(sp + 1);
 
 	/* find environ */
-	environ = envp = argv + argc + 1;
+	envp = argv + argc + 1;
 
 	/* find _auxv */
 	for (auxv = (void *)envp; *auxv++;)
 		;
-	_auxv = auxv;
 
-	for (ctor_func = __preinit_array_start; ctor_func < __preinit_array_end; ctor_func++)
-		(*ctor_func)(argc, argv, envp);
-	for (ctor_func = __init_array_start; ctor_func < __init_array_end; ctor_func++)
-		(*ctor_func)(argc, argv, envp);
-
-	/* go to application */
-	exitcode = _nolibc_main(argc, argv, envp);
-
-	for (dtor_func = __fini_array_end; dtor_func > __fini_array_start;)
-		(*--dtor_func)();
-
-	exit(exitcode);
+	__start_c(argc, argv, envp, auxv);
 }
 
 #endif /* NOLIBC_NO_RUNTIME */
-- 
2.51.0
Re: [RFC PATCH 2/9] tools/nolibc: crt: Split _start_c() into stack-only and normal part
Posted by Thomas Weißschuh 1 week ago
On 2026-01-31 16:44:33+0900, Daniel Palmer wrote:
> To prepare for nolibc programs being able to relocate themselves
> we need to split _start_c() into two parts:
> 
> - One part that only uses the stack so there are no accesses via
>   the GOT etc that isn't setup yet. Note that on m68k at least
>   this also means forcing the stackprotector off because accessing
>   the stack protector canary is done via the GOT.
> 
> - Another part that is called after we have done relocation so it
>   is safe to access global variables etc that might use the GOT etc.
> 
> Signed-off-by: Daniel Palmer <daniel@thingy.jp>
> ---
>  tools/include/nolibc/crt.h | 57 +++++++++++++++++++++++---------------
>  1 file changed, 35 insertions(+), 22 deletions(-)
> 
> diff --git a/tools/include/nolibc/crt.h b/tools/include/nolibc/crt.h
> index d9262998dae9..899062c00fb7 100644
> --- a/tools/include/nolibc/crt.h
> +++ b/tools/include/nolibc/crt.h
> @@ -27,26 +27,51 @@ extern void (*const __init_array_end[])(int, char **, char**) __attribute__((wea
>  extern void (*const __fini_array_start[])(void) __attribute__((weak));
>  extern void (*const __fini_array_end[])(void) __attribute__((weak));
>  
> -void _start_c(long *sp);
> -__attribute__((weak,used))
> +void __start_c(long argc, char **argv, char **envp, const unsigned long *auxv);
> +__attribute__((weak, used))

We now have an additional never-inlined, global function.
This should stay inlinable in the regular case.

>  #if __nolibc_has_feature(undefined_behavior_sanitizer)
>  	__attribute__((no_sanitize("function")))
>  #endif
> -void _start_c(long *sp)
> +void __no_stack_protector __start_c(long argc, char **argv, char **envp, const unsigned long *auxv)

Please some better naming.

_start_c_global_data() or something.

>  {
> -	long argc;
> -	char **argv;
> -	char **envp;
> -	int exitcode;
>  	void (* const *ctor_func)(int, char **, char **);
>  	void (* const *dtor_func)(void);
> -	const unsigned long *auxv;
>  	/* silence potential warning: conflicting types for 'main' */
>  	int _nolibc_main(int, char **, char **) __asm__ ("main");
> +	int exitcode;
>  
>  	/* initialize stack protector */
>  	__stack_chk_init();
>  
> +	environ = envp;
> +	_auxv = auxv;
> +
> +	for (ctor_func = __preinit_array_start; ctor_func < __preinit_array_end; ctor_func++)
> +		(*ctor_func)(argc, argv, envp);
> +	for (ctor_func = __init_array_start; ctor_func < __init_array_end; ctor_func++)
> +		(*ctor_func)(argc, argv, envp);
> +
> +	/* go to application */
> +	exitcode = _nolibc_main(argc, argv, envp);
> +
> +	for (dtor_func = __fini_array_end; dtor_func > __fini_array_start;)
> +		(*--dtor_func)();
> +
> +	exit(exitcode);
> +}
> +
> +void _start_c(long *sp);
> +__attribute__((weak, used))
> +#if __nolibc_has_feature(undefined_behavior_sanitizer)
> +	__attribute__((no_sanitize("function")))
> +#endif

Now that this is repeated, it should be behind a #define.

> +void __no_stack_protector _start_c(long *sp)
> +{

(...)