Re: [PATCH RFC] tools/nolibc: add support for constructors and destructors

From: Thomas Weißschuh
Date: Sat Oct 07 2023 - 03:29:02 EST


Hi Willy,

On 2023-10-07 08:50:25+0200, Willy Tarreau wrote:
> On Thu, Oct 05, 2023 at 06:45:07PM +0200, Thomas Weißschuh wrote:
> > With the startup code moved to C, implementing support for
> > constructors and deconstructors is fairly easy to implement.

> [..]

> > diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
> > index a3ee4496bf0a..f166b425613a 100644
> > --- a/tools/testing/selftests/nolibc/nolibc-test.c
> > +++ b/tools/testing/selftests/nolibc/nolibc-test.c
> > @@ -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,18 @@ int expect_strne(const char *expr, int llen, const char *cmp)
> > #define CASE_TEST(name) \
> > case __LINE__: llen += printf("%d %s", test, #name);
> >
> > +__attribute__((constructor))
> > +static void constructor1(void)
> > +{
> > + constructor_test_value = 1;
> > +}
> > +
> > +__attribute__((constructor))
> > +static void constructor2(void)
> > +{
> > + constructor_test_value *= 2;
> > +}
> > +
>
> In the past I learned the hard way that you can never trust the execution
> order of constructors, so if you're unlucky above you could very well end
> up with 1 and that would be correct. I suggest that instead you do something
> such as:
>
> constructor_test_value += 1;
> ...
> constructor_test_value += 2;
>
> and check for value 3 in the test to make sure they were both executed
> exactly once each.

Was this indeterminism for constructors from the same translation unit?
Or across different translation units/shared objects?


I'm not entirely sure, but the GCC [0] docs could be read that within a
given TU the execution order for constructors is the same as the
definition order, even for C.

The priorities for constructor and destructor functions are the same
as those specified for namespace-scope C++ objects

And linked from there:

In Standard C++, objects defined at namespace scope are guaranteed
to be initialized in an order in strict accordance with that of
their definitions *in a given translation unit*. No guarantee is made
for initializations across translation units.


[0] https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
(using an old version of the docs to make sure this didn't change recently)