clang creates global symbols; gcc creates local symbols

Viewed 31

I'm porting a program from gcc to clang, and ran into a strange difference in behavior:

enum struct E : unsigned  char {
    zero  =  0,
    one   =  1,
    two   =  2,
    three =  3,
    answ  = 42,
};

template<E> constexpr int doubler =  -1;

template<> constexpr int doubler<E::zero>  =  0;
template<> constexpr int doubler<E::one>   =  2;
template<> constexpr int doubler<E::two>   =  4;
template<> constexpr int doubler<E::three> =  6;
template<> constexpr int doubler<E::answ>  = 84;

After compiling this code with various compilers (packaged compilers in WSL Ubuntu 20.04.4 LTS), I got slightly different object files from gcc and clang:

$ for c  in g++     g++-10  g++-8   g++-9 clang++-10  clang++-12 ; do $c -c -Wall -Wpedantic -Wextra -std=c++17 meow.cpp -o meow-$c.o ; done
$ nm -C *.o

meow-clang++-10.o:
0000000000000000 R doubler<(E)0>
0000000000000004 R doubler<(E)1>
0000000000000008 R doubler<(E)2>
000000000000000c R doubler<(E)3>
0000000000000010 R doubler<(E)42>

meow-clang++-12.o:
0000000000000000 R doubler<(E)0>
0000000000000004 R doubler<(E)1>
0000000000000008 R doubler<(E)2>
000000000000000c R doubler<(E)3>
0000000000000010 R doubler<(E)42>

meow-g++-10.o:
0000000000000000 r doubler<(E)0>
0000000000000004 r doubler<(E)1>
0000000000000008 r doubler<(E)2>
000000000000000c r doubler<(E)3>
0000000000000010 r doubler<(E)42>

meow-g++-8.o:
0000000000000000 r doubler<(E)0>
0000000000000004 r doubler<(E)1>
0000000000000008 r doubler<(E)2>
000000000000000c r doubler<(E)3>
0000000000000010 r doubler<(E)42>

meow-g++-9.o:
0000000000000000 r doubler<(E)0>
0000000000000004 r doubler<(E)1>
0000000000000008 r doubler<(E)2>
000000000000000c r doubler<(E)3>
0000000000000010 r doubler<(E)42>

meow-g++.o:
0000000000000000 r doubler<(E)0>
0000000000000004 r doubler<(E)1>
0000000000000008 r doubler<(E)2>
000000000000000c r doubler<(E)3>
0000000000000010 r doubler<(E)42>
$ readelf -s meow*.o | egrep 'File|doubler' | c++filt
File: meow-clang++-10.o
     2: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)0>
     3: 0000000000000004     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)1>
     4: 0000000000000008     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)2>
     5: 000000000000000c     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)3>
     6: 0000000000000010     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)42>
File: meow-clang++-12.o
     2: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)0>
     3: 0000000000000004     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)1>
     4: 0000000000000008     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)2>
     5: 000000000000000c     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)3>
     6: 0000000000000010     4 OBJECT  GLOBAL DEFAULT    3 doubler<(E)42>
File: meow-g++-10.o
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)0>
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)1>
     8: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)2>
     9: 000000000000000c     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)3>
    10: 0000000000000010     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)42>
File: meow-g++-8.o
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)0>
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)1>
     8: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)2>
     9: 000000000000000c     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)3>
    10: 0000000000000010     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)42>
File: meow-g++-9.o
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)0>
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)1>
     8: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)2>
     9: 000000000000000c     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)3>
    10: 0000000000000010     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)42>
File: meow-g++.o
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)0>
     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)1>
     8: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)2>
     9: 000000000000000c     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)3>
    10: 0000000000000010     4 OBJECT  LOCAL  DEFAULT    4 doubler<(E)42>

Why does clang create symbols with GLOBAL binding, when gcc creates symbols with LOCAL binding?

There doesn't seem to be a significant difference in the assembly generated by gcc and clang (https://godbolt.org/z/K8fcaWMT7), so why the difference in the symbols in the object files? Is this a difference between llvm-as and gas?

How can I make clang create symbols with LOCAL binding?

In real life, these variables are defined in a header, so my situation is similar to Duplicate symbols with global template variable using Clang . The header is included from multiple translation units, so the program violates the One Definition Rule. GCC compiles and links the program, but linking objects compiled with clang fails with multiple definitions of these symbols.

Unfortunately, the header is not under my control (the proper fix would be to get rid of the ODR violation using inline or an unnamed namespace), so it would be useful if at least I could get the program to link until the header is fixed.

0 Answers
Related