Jerome Forissier via llvm-dev
2020-Jun-12 08:05 UTC
[llvm-dev] Issue with __attribute__((constructor)) and -Os -fno-common
On 6/11/20 11:25 PM, James Y Knight wrote:> The global constructor was removed by setting the initial value of "val" to > 1 instead of 0. So, the behavior of this program is preserved. Doesn't look > like erroneous behavior.OK, my example is too simplified indeed. Please consider the following instead: int val; static void __attribute__((constructor)) init_fn(void) { val++; } int main(int argc, char *argv[]) { return val; } With this, clang -Os -fno-common generates a global variable initialized to 1 and discards init_fn(). Now, what happens if the executable is later linked against a shared library which has its own constructor and sets "val" to some non-zero value? I mean this for instance: extern int val; static void __attribute__((constructor)) so_init_fn(void) { val = 1; } I would expect the main program to return 2, not 1. Last thing, if I define "val" as volatile, the programs behaves as expected. Are we in "unspecified, just don't do this" territory here? Thanks, -- Jerome> > On Thu, Jun 11, 2020 at 10:12 AM Jerome Forissier via llvm-dev < > llvm-dev at lists.llvm.org> wrote: > >> Hi, >> >> I think that Clang erroneously discards a function annotated with >> __attribute__((constructor)) when flags -Os -fno-common are given. Test >> case below. >> >> What do you think? >> >> Thanks. >> >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> $ cat ctor.c >> int val; >> >> static void __attribute__((constructor)) init_fn(void) >> { >> val = 1; >> } >> >> int main(int argc, char *argv[]) >> { >> return val; >> } >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> >> Here is what I observed: >> >> - Clang (10.0.0-4ubuntu1) with -Os -fno-common: function init_fn() is >> NOT emitted, >> - Clang (10.0.0-4ubuntu1) with no flag, or only -Os or -fno-common: >> init_fn() is present as expected, >> - GCC (Ubuntu 9.3.0-10ubuntu1) with the same flags: init_fn() is present >> too, >> - Since https://reviews.llvm.org/D75056, -fno-common is the default and >> therefore -Os is enough to cause the issue. >> >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> $ clang --target=arm-linux-gnueabihf -Os -fno-common -S ctor.c \ >> -o /dev/stdout | grep init_fn >> $ clang --target=arm-linux-gnueabihf -Os -S ctor.c \ >> -o /dev/stdout | grep init_fn >> .p2align 2 @ -- Begin function init_fn >> .type init_fn,%function >> .code 32 @ @init_fn >> init_fn: >> .size init_fn, .Lfunc_end0-init_fn >> .long init_fn(target1) >> .addrsig_sym init_fn >> $ clang --target=arm-linux-gnueabihf -fno-common -S ctor.c \ >> -o /dev/stdout | grep init_fn >> .p2align 2 @ -- Begin function init_fn >> .type init_fn,%function >> .code 32 @ @init_fn >> init_fn: >> .size init_fn, .Lfunc_end0-init_fn >> .long init_fn(target1) >> .addrsig_sym init_fn >> $ arm-linux-gnueabihf-gcc -Os -fno-common -S ctor.c \ >> -o /dev/stdout | grep init_fn >> .type init_fn, %function >> init_fn: >> .size init_fn, .-init_fn >> .word init_fn(target1) >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> >> -- >> Jerome >> _______________________________________________ >> LLVM Developers mailing list >> llvm-dev at lists.llvm.org >> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >> >
Mehdi AMINI via llvm-dev
2020-Jun-12 21:26 UTC
[llvm-dev] Issue with __attribute__((constructor)) and -Os -fno-common
On Fri, Jun 12, 2020 at 1:05 AM Jerome Forissier via llvm-dev < llvm-dev at lists.llvm.org> wrote:> > > On 6/11/20 11:25 PM, James Y Knight wrote: > > The global constructor was removed by setting the initial value of "val" > to > > 1 instead of 0. So, the behavior of this program is preserved. Doesn't > look > > like erroneous behavior. > > OK, my example is too simplified indeed. Please consider the following > instead: > > int val; > > static void __attribute__((constructor)) init_fn(void) > { > val++; > } > > int main(int argc, char *argv[]) > { > return val; > } > > With this, clang -Os -fno-common generates a global variable initialized > to 1 and discards init_fn(). > > Now, what happens if the executable is later linked against a shared > library which has its own constructor and sets "val" to some non-zero > value? I mean this for instance: > > extern int val; > > static void __attribute__((constructor)) so_init_fn(void) > { > val = 1; > } > > I would expect the main program to return 2, not 1. >It seems to me that there is no ordering guarantee that your so_init_fn() would run before init_fn(), isn't this a case of "static initialization order fiasco"? <https://www.google.com/search?client=safari&rls=en&q=static+initialization+order+fiasco&ie=UTF-8&oe=UTF-8>> > Last thing, if I define "val" as volatile, the programs behaves as > expected. > > Are we in "unspecified, just don't do this" territory here? > > Thanks, > -- > Jerome > > > > > On Thu, Jun 11, 2020 at 10:12 AM Jerome Forissier via llvm-dev < > > llvm-dev at lists.llvm.org> wrote: > > > >> Hi, > >> > >> I think that Clang erroneously discards a function annotated with > >> __attribute__((constructor)) when flags -Os -fno-common are given. Test > >> case below. > >> > >> What do you think? > >> > >> Thanks. > >> > >> ----8<--------8<--------8<--------8<--------8<--------8<-------- > >> $ cat ctor.c > >> int val; > >> > >> static void __attribute__((constructor)) init_fn(void) > >> { > >> val = 1; > >> } > >> > >> int main(int argc, char *argv[]) > >> { > >> return val; > >> } > >> ----8<--------8<--------8<--------8<--------8<--------8<-------- > >> > >> Here is what I observed: > >> > >> - Clang (10.0.0-4ubuntu1) with -Os -fno-common: function init_fn() is > >> NOT emitted, > >> - Clang (10.0.0-4ubuntu1) with no flag, or only -Os or -fno-common: > >> init_fn() is present as expected, > >> - GCC (Ubuntu 9.3.0-10ubuntu1) with the same flags: init_fn() is present > >> too, > >> - Since https://reviews.llvm.org/D75056, -fno-common is the default and > >> therefore -Os is enough to cause the issue. > >> > >> ----8<--------8<--------8<--------8<--------8<--------8<-------- > >> $ clang --target=arm-linux-gnueabihf -Os -fno-common -S ctor.c \ > >> -o /dev/stdout | grep init_fn > >> $ clang --target=arm-linux-gnueabihf -Os -S ctor.c \ > >> -o /dev/stdout | grep init_fn > >> .p2align 2 @ -- Begin function init_fn > >> .type init_fn,%function > >> .code 32 @ @init_fn > >> init_fn: > >> .size init_fn, .Lfunc_end0-init_fn > >> .long init_fn(target1) > >> .addrsig_sym init_fn > >> $ clang --target=arm-linux-gnueabihf -fno-common -S ctor.c \ > >> -o /dev/stdout | grep init_fn > >> .p2align 2 @ -- Begin function init_fn > >> .type init_fn,%function > >> .code 32 @ @init_fn > >> init_fn: > >> .size init_fn, .Lfunc_end0-init_fn > >> .long init_fn(target1) > >> .addrsig_sym init_fn > >> $ arm-linux-gnueabihf-gcc -Os -fno-common -S ctor.c \ > >> -o /dev/stdout | grep init_fn > >> .type init_fn, %function > >> init_fn: > >> .size init_fn, .-init_fn > >> .word init_fn(target1) > >> ----8<--------8<--------8<--------8<--------8<--------8<-------- > >> > >> -- > >> Jerome > >> _______________________________________________ > >> LLVM Developers mailing list > >> llvm-dev at lists.llvm.org > >> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev > >> > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200612/1589af51/attachment.html>
Fangrui Song via llvm-dev
2020-Jun-14 19:18 UTC
[llvm-dev] Issue with __attribute__((constructor)) and -Os -fno-common
On 2020-06-12, Mehdi AMINI via llvm-dev wrote:>On Fri, Jun 12, 2020 at 1:05 AM Jerome Forissier via llvm-dev < >llvm-dev at lists.llvm.org> wrote: > >> >> >> On 6/11/20 11:25 PM, James Y Knight wrote: >> > The global constructor was removed by setting the initial value of "val" >> to >> > 1 instead of 0. So, the behavior of this program is preserved. Doesn't >> look >> > like erroneous behavior. >> >> OK, my example is too simplified indeed. Please consider the following >> instead: >> >> int val; >> >> static void __attribute__((constructor)) init_fn(void) >> { >> val++; >> } >> >> int main(int argc, char *argv[]) >> { >> return val; >> } >> >> With this, clang -Os -fno-common generates a global variable initialized >> to 1 and discards init_fn(). >> >> Now, what happens if the executable is later linked against a shared >> library which has its own constructor and sets "val" to some non-zero >> value? I mean this for instance: >> >> extern int val; >> >> static void __attribute__((constructor)) so_init_fn(void) >> { >> val = 1; >> } >> >> I would expect the main program to return 2, not 1. >> > >It seems to me that there is no ordering guarantee that your so_init_fn() >would run before init_fn(), isn't this a case of "static initialization >order fiasco"? ><https://www.google.com/search?client=safari&rls=en&q=static+initialization+order+fiasco&ie=UTF-8&oe=UTF-8>I tend to agree that clang has a bug. __attribute__((constructor)) is an extension to the C++ standard. Even if C++ defined this and said this were an undefined behavior, an implementation can augment the C++ standard by providing a definition of an undefined behavior. On ELF, the implementation uses Initialization and Termination Functions, which are subject to http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#init_fini "Before the initialization functions for any object A is called, the initialization functions for any other objects that object A depends on are called." The ld.so implementations use a post-order traversal of DT_NEEDED dependencies. I tend to think the intention of the function attribute (https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes) is to conform to the ELF specification. This is a property users can reliably depend on. In Jerome's example, it is guaranteed that the initialization function in the shared object runs before the one in the main executable. clang should not optimize out the initialization function to alter the observed program behavior.>> >> Last thing, if I define "val" as volatile, the programs behaves as >> expected. >> >> Are we in "unspecified, just don't do this" territory here? >> >> Thanks, >> -- >> Jerome >> >> > >> > On Thu, Jun 11, 2020 at 10:12 AM Jerome Forissier via llvm-dev < >> > llvm-dev at lists.llvm.org> wrote: >> > >> >> Hi, >> >> >> >> I think that Clang erroneously discards a function annotated with >> >> __attribute__((constructor)) when flags -Os -fno-common are given. Test >> >> case below. >> >> >> >> What do you think? >> >> >> >> Thanks. >> >> >> >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> >> $ cat ctor.c >> >> int val; >> >> >> >> static void __attribute__((constructor)) init_fn(void) >> >> { >> >> val = 1; >> >> } >> >> >> >> int main(int argc, char *argv[]) >> >> { >> >> return val; >> >> } >> >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> >> >> >> Here is what I observed: >> >> >> >> - Clang (10.0.0-4ubuntu1) with -Os -fno-common: function init_fn() is >> >> NOT emitted, >> >> - Clang (10.0.0-4ubuntu1) with no flag, or only -Os or -fno-common: >> >> init_fn() is present as expected, >> >> - GCC (Ubuntu 9.3.0-10ubuntu1) with the same flags: init_fn() is present >> >> too, >> >> - Since https://reviews.llvm.org/D75056, -fno-common is the default and >> >> therefore -Os is enough to cause the issue. >> >> >> >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> >> $ clang --target=arm-linux-gnueabihf -Os -fno-common -S ctor.c \ >> >> -o /dev/stdout | grep init_fn >> >> $ clang --target=arm-linux-gnueabihf -Os -S ctor.c \ >> >> -o /dev/stdout | grep init_fn >> >> .p2align 2 @ -- Begin function init_fn >> >> .type init_fn,%function >> >> .code 32 @ @init_fn >> >> init_fn: >> >> .size init_fn, .Lfunc_end0-init_fn >> >> .long init_fn(target1) >> >> .addrsig_sym init_fn >> >> $ clang --target=arm-linux-gnueabihf -fno-common -S ctor.c \ >> >> -o /dev/stdout | grep init_fn >> >> .p2align 2 @ -- Begin function init_fn >> >> .type init_fn,%function >> >> .code 32 @ @init_fn >> >> init_fn: >> >> .size init_fn, .Lfunc_end0-init_fn >> >> .long init_fn(target1) >> >> .addrsig_sym init_fn >> >> $ arm-linux-gnueabihf-gcc -Os -fno-common -S ctor.c \ >> >> -o /dev/stdout | grep init_fn >> >> .type init_fn, %function >> >> init_fn: >> >> .size init_fn, .-init_fn >> >> .word init_fn(target1) >> >> ----8<--------8<--------8<--------8<--------8<--------8<-------- >> >> >> >> -- >> >> Jerome >> >> _______________________________________________ >> >> LLVM Developers mailing list >> >> llvm-dev at lists.llvm.org >> >> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >> >> >> > >> _______________________________________________ >> LLVM Developers mailing list >> llvm-dev at lists.llvm.org >> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >>>_______________________________________________ >LLVM Developers mailing list >llvm-dev at lists.llvm.org >https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev