tools/nolibc: add support for constructors and destructors

With the startup code moved to C, implementing support for
constructors and deconstructors is fairly easy to implement.

Examples for code size impact:

   text	   data	    bss	    dec	    hex	filename
  21837	    104	     88	  22029	   560d	nolibc-test.before
  22135	    120	     88	  22343	   5747	nolibc-test.after
  21970	    104	     88	  22162	   5692 nolibc-test.after-only-crt.h-changes

The sections are defined by [0].

[0] https://refspecs.linuxfoundation.org/elf/gabi4+/ch5.dynamic.html

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Acked-by: Willy Tarreau <w@1wt.eu>
Link: https://lore.kernel.org/lkml/20231007-nolibc-constructors-v2-1-ef84693efbc1@weissschuh.net/
This commit is contained in:
Thomas Weißschuh 2023-10-05 18:17:29 +02:00
parent eddfc3c742
commit 63aa531716
2 changed files with 39 additions and 1 deletions

View File

@ -13,12 +13,23 @@ const unsigned long *_auxv __attribute__((weak));
static void __stack_chk_init(void);
static void exit(int);
extern void (*const __preinit_array_start[])(void) __attribute__((weak));
extern void (*const __preinit_array_end[])(void) __attribute__((weak));
extern void (*const __init_array_start[])(void) __attribute__((weak));
extern void (*const __init_array_end[])(void) __attribute__((weak));
extern void (*const __fini_array_start[])(void) __attribute__((weak));
extern void (*const __fini_array_end[])(void) __attribute__((weak));
__attribute__((weak))
void _start_c(long *sp)
{
long argc;
char **argv;
char **envp;
int exitcode;
void (* const *func)(void);
const unsigned long *auxv;
/* silence potential warning: conflicting types for 'main' */
int _nolibc_main(int, char **, char **) __asm__ ("main");
@ -55,8 +66,18 @@ void _start_c(long *sp)
;
_auxv = auxv;
for (func = __preinit_array_start; func < __preinit_array_end; func++)
(*func)();
for (func = __init_array_start; func < __init_array_end; func++)
(*func)();
/* go to application */
exit(_nolibc_main(argc, argv, envp));
exitcode = _nolibc_main(argc, argv, envp);
for (func = __fini_array_end; func > __fini_array_start;)
(*--func)();
exit(exitcode);
}
#endif /* _NOLIBC_CRT_H */

View File

@ -57,6 +57,9 @@ static int test_argc;
/* will be used by some test cases as readable file, please don't write it */
static const char *argv0;
/* will be used by constructor tests */
static int constructor_test_value;
/* definition of a series of tests */
struct test {
const char *name; /* test name */
@ -594,6 +597,19 @@ int expect_strne(const char *expr, int llen, const char *cmp)
#define CASE_TEST(name) \
case __LINE__: llen += printf("%d %s", test, #name);
/* constructors validate that they are executed in definition order */
__attribute__((constructor))
static void constructor1(void)
{
constructor_test_value = 1;
}
__attribute__((constructor))
static void constructor2(void)
{
constructor_test_value *= 2;
}
int run_startup(int min, int max)
{
int test;
@ -630,6 +646,7 @@ int run_startup(int min, int max)
CASE_TEST(environ_HOME); EXPECT_PTRNZ(1, getenv("HOME")); break;
CASE_TEST(auxv_addr); EXPECT_PTRGT(test_auxv != (void *)-1, test_auxv, brk); break;
CASE_TEST(auxv_AT_UID); EXPECT_EQ(1, getauxval(AT_UID), getuid()); break;
CASE_TEST(constructor); EXPECT_EQ(1, constructor_test_value, 2); break;
case __LINE__:
return ret; /* must be last */
/* note: do not set any defaults so as to permit holes above */