<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jun 14, 2020 at 12:18 PM Fangrui Song <<a href="mailto:maskray@google.com">maskray@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">On 2020-06-12, Mehdi AMINI via llvm-dev wrote:<br>
>On Fri, Jun 12, 2020 at 1:05 AM Jerome Forissier via llvm-dev <<br>
><a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>> wrote:<br>
><br>
>><br>
>><br>
>> On 6/11/20 11:25 PM, James Y Knight wrote:<br>
>> > The global constructor was removed by setting the initial value of "val"<br>
>> to<br>
>> > 1 instead of 0. So, the behavior of this program is preserved. Doesn't<br>
>> look<br>
>> > like erroneous behavior.<br>
>><br>
>> OK, my example is too simplified indeed. Please consider the following<br>
>> instead:<br>
>><br>
>> int val;<br>
>><br>
>> static void __attribute__((constructor)) init_fn(void)<br>
>> {<br>
>> val++;<br>
>> }<br>
>><br>
>> int main(int argc, char *argv[])<br>
>> {<br>
>> return val;<br>
>> }<br>
>><br>
>> With this, clang -Os -fno-common generates a global variable initialized<br>
>> to 1 and discards init_fn().<br>
>><br>
>> Now, what happens if the executable is later linked against a shared<br>
>> library which has its own constructor and sets "val" to some non-zero<br>
>> value? I mean this for instance:<br>
>><br>
>> extern int val;<br>
>><br>
>> static void __attribute__((constructor)) so_init_fn(void)<br>
>> {<br>
>> val = 1;<br>
>> }<br>
>><br>
>> I would expect the main program to return 2, not 1.<br>
>><br>
><br>
>It seems to me that there is no ordering guarantee that your so_init_fn()<br>
>would run before init_fn(), isn't this a case of "static initialization<br>
>order fiasco"?<br>
><<a href="https://www.google.com/search?client=safari&rls=en&q=static+initialization+order+fiasco&ie=UTF-8&oe=UTF-8" rel="noreferrer" target="_blank">https://www.google.com/search?client=safari&rls=en&q=static+initialization+order+fiasco&ie=UTF-8&oe=UTF-8</a>><br>
<br>
I tend to agree that clang has a bug.<br>
<br>
__attribute__((constructor)) is an extension to the C++ standard.<br>
Even if C++ defined this and said this were an undefined behavior,<br>
an implementation can augment the C++ standard by providing a definition<br>
of an undefined behavior.<br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<br>
On ELF, the implementation uses Initialization and Termination<br>
Functions, which are subject to<br>
<a href="http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#init_fini" rel="noreferrer" target="_blank">http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#init_fini</a><br>
<br>
"Before the initialization functions for any object A is called, the<br>
initialization functions for any other objects that object A depends on<br>
are called." </blockquote><div><br></div><div>In this case though the "object" does not depend on any other object here I believe. Even if it did, the behavior of the loader does not put constraint on the source/language semantics that the compiler operates with: it only puts guarantee on the output of the compiler.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">The ld.so implementations use a post-order traversal of DT_NEEDED<br>
dependencies.<br>
I tend to think the intention of the function attribute<br>
(<a href="https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes" rel="noreferrer" target="_blank">https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes</a>)<br>
is to conform to the ELF specification. This is a property users can<br>
reliably depend on.<br></blockquote><div><br></div><div>I don't read it the same way: this doc to me indicates that it is an extension to C that provides the same behavior as a C++ global constructor. I don't see here anything that put a restriction on the cross-object initialization order. </div><div>The fact that the platform (ELF or another) provides a restriction does not automatically translate to the higher-level.</div><div><div style="color:rgb(0,0,0)"> </div></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<br>
In Jerome's example, it is guaranteed that the initialization function<br>
in the shared object runs before the one in the main executable.<br>
clang should not optimize out the initialization function to alter the<br>
observed program behavior.<br></blockquote><div><br></div><div>I see it similarly to the different choice clang and gcc are making on the "interposible" property and assumptions, isn't it? See: <a href="http://lists.llvm.org/pipermail/llvm-dev/2016-November/107625.html">http://lists.llvm.org/pipermail/llvm-dev/2016-November/107625.html</a></div><div><br></div><div>Actually taking the example above, adding -fsemantic-interposition shows the difference: with the flag it behaves as you'd expect: <a href="https://godbolt.org/z/fC5yzY">https://godbolt.org/z/fC5yzY</a> ; however not for the reason you're arguing for.</div><div><br></div><div><br></div><div>-- </div><div>Mehdi</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<br>
>><br>
>> Last thing, if I define "val" as volatile, the programs behaves as<br>
>> expected.<br>
>><br>
>> Are we in "unspecified, just don't do this" territory here?<br>
>><br>
>> Thanks,<br>
>> --<br>
>> Jerome<br>
>><br>
>> ><br>
>> > On Thu, Jun 11, 2020 at 10:12 AM Jerome Forissier via llvm-dev <<br>
>> > <a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>> wrote:<br>
>> ><br>
>> >> Hi,<br>
>> >><br>
>> >> I think that Clang erroneously discards a function annotated with<br>
>> >> __attribute__((constructor)) when flags -Os -fno-common are given. Test<br>
>> >> case below.<br>
>> >><br>
>> >> What do you think?<br>
>> >><br>
>> >> Thanks.<br>
>> >><br>
>> >> ----8<--------8<--------8<--------8<--------8<--------8<--------<br>
>> >> $ cat ctor.c<br>
>> >> int val;<br>
>> >><br>
>> >> static void __attribute__((constructor)) init_fn(void)<br>
>> >> {<br>
>> >> val = 1;<br>
>> >> }<br>
>> >><br>
>> >> int main(int argc, char *argv[])<br>
>> >> {<br>
>> >> return val;<br>
>> >> }<br>
>> >> ----8<--------8<--------8<--------8<--------8<--------8<--------<br>
>> >><br>
>> >> Here is what I observed:<br>
>> >><br>
>> >> - Clang (10.0.0-4ubuntu1) with -Os -fno-common: function init_fn() is<br>
>> >> NOT emitted,<br>
>> >> - Clang (10.0.0-4ubuntu1) with no flag, or only -Os or -fno-common:<br>
>> >> init_fn() is present as expected,<br>
>> >> - GCC (Ubuntu 9.3.0-10ubuntu1) with the same flags: init_fn() is present<br>
>> >> too,<br>
>> >> - Since <a href="https://reviews.llvm.org/D75056" rel="noreferrer" target="_blank">https://reviews.llvm.org/D75056</a>, -fno-common is the default and<br>
>> >> therefore -Os is enough to cause the issue.<br>
>> >><br>
>> >> ----8<--------8<--------8<--------8<--------8<--------8<--------<br>
>> >> $ clang --target=arm-linux-gnueabihf -Os -fno-common -S ctor.c \<br>
>> >> -o /dev/stdout | grep init_fn<br>
>> >> $ clang --target=arm-linux-gnueabihf -Os -S ctor.c \<br>
>> >> -o /dev/stdout | grep init_fn<br>
>> >> .p2align 2 @ -- Begin function init_fn<br>
>> >> .type init_fn,%function<br>
>> >> .code 32 @ @init_fn<br>
>> >> init_fn:<br>
>> >> .size init_fn, .Lfunc_end0-init_fn<br>
>> >> .long init_fn(target1)<br>
>> >> .addrsig_sym init_fn<br>
>> >> $ clang --target=arm-linux-gnueabihf -fno-common -S ctor.c \<br>
>> >> -o /dev/stdout | grep init_fn<br>
>> >> .p2align 2 @ -- Begin function init_fn<br>
>> >> .type init_fn,%function<br>
>> >> .code 32 @ @init_fn<br>
>> >> init_fn:<br>
>> >> .size init_fn, .Lfunc_end0-init_fn<br>
>> >> .long init_fn(target1)<br>
>> >> .addrsig_sym init_fn<br>
>> >> $ arm-linux-gnueabihf-gcc -Os -fno-common -S ctor.c \<br>
>> >> -o /dev/stdout | grep init_fn<br>
>> >> .type init_fn, %function<br>
>> >> init_fn:<br>
>> >> .size init_fn, .-init_fn<br>
>> >> .word init_fn(target1)<br>
>> >> ----8<--------8<--------8<--------8<--------8<--------8<--------<br>
>> >><br>
>> >> --<br>
>> >> Jerome<br>
>> >> _______________________________________________<br>
>> >> LLVM Developers mailing list<br>
>> >> <a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
>> >> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
>> >><br>
>> ><br>
>> _______________________________________________<br>
>> LLVM Developers mailing list<br>
>> <a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
>> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
>><br>
<br>
>_______________________________________________<br>
>LLVM Developers mailing list<br>
><a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
><a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
<br>
</blockquote></div></div></div></div></div></div>