http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52455
Bug #: 52455 Summary: Symbolic constant treated as extern when -O not specified resulting in undefined symbol at link Classification: Unclassified Product: gcc Version: 4.7.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ AssignedTo: unassig...@gcc.gnu.org ReportedBy: sc...@lurndal.org AMD64/Linux. First noticed with 4.4.1. Tried 4.7.1 to see if it had been fixed in the meantime (svn trunk as of 1-mar-2012). An unsigned long long static const private value in a class is used in an inline function. When the compilation unit which references the inline function is compiled without optimization (sans -O), the references to the static const values are externs (which cause the link-edit step to fail with unresolved symbols). When compiled with -O1 or greater, the compiler correctly inserts the 64-bit constants as literals using movabsq and the link-edit step succeeds. If the "?:" syntax in c_test::fault() is replaced with an if statement, the correct code is generated in both cases. Which should help point towards the bug. Using built-in specs. COLLECT_GCC=/usr/local/bin/g++-4.6 Target: x86_64-linux-gnu Configured with: ../gcc_tot/configure -v --enable-languages=c,c++ --prefix=/usr/local --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/local/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/local/include/c++/4.6 --program-suffix=-4.6 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-gmp=/usr/local --with-mpfr=/usr/local --with-mpc=/usr/local/ Thread model: posix gcc version 4.7.0 20120301 (experimental) (GCC) COLLECT_GCC_OPTIONS='-v' '-save-temps' '-c' '-o' '/tmp/a.o' '-shared-libgcc' '-mtune=generic' '-march=x86-64' /usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/cc1plus -E -quiet -v -D_GNU_SOURCE /tmp/a.cpp -mtune=generic -march=x86-64 -fpch-preprocess -o a.ii ignoring nonexistent directory "/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/../../../../x86_64-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/local/include/c++/4.6 /usr/local/include/c++/4.6/x86_64-linux-gnu /usr/local/include/c++/4.6/backward /usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/include /usr/local/include /usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/include-fixed /usr/include End of search list. COLLECT_GCC_OPTIONS='-v' '-save-temps' '-c' '-o' '/tmp/a.o' '-shared-libgcc' '-mtune=generic' '-march=x86-64' /usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/cc1plus -fpreprocessed a.ii -quiet -dumpbase a.cpp -mtune=generic -march=x86-64 -auxbase-strip /tmp/a.o -version -o a.s GNU C++ (GCC) version 4.7.0 20120301 (experimental) (x86_64-linux-gnu) compiled by GNU C version 4.7.0 20120301 (experimental), GMP version 5.0.4, MPFR version 3.1.0, MPC version 0.9 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 GNU C++ (GCC) version 4.7.0 20120301 (experimental) (x86_64-linux-gnu) compiled by GNU C version 4.7.0 20120301 (experimental), GMP version 5.0.4, MPFR version 3.1.0, MPC version 0.9 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: dcdba1f80966b019e0b78518616aba28 COLLECT_GCC_OPTIONS='-v' '-save-temps' '-c' '-o' '/tmp/a.o' '-shared-libgcc' '-mtune=generic' '-march=x86-64' as --64 -o /tmp/a.o a.s COMPILER_PATH=/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/:/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/:/usr/local/lib/gcc/x86_64-linux-gnu/:/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/:/usr/local/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/:/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/local/lib/gcc/x86_64-linux-gnu/4.7.0/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-save-temps' '-c' '-o' '/tmp/a.o' '-shared-libgcc' '-mtune=generic' '-march=x86-64' The source files: a.h ------------------- class c_test { static const unsigned long long TEST1 = 0x8000000000000000ull; static const unsigned long long TEST2 = 0x4000000000000000ull; unsigned long long t_value; public: c_test(void) { t_value = 0; } void fault(bool hard); unsigned long long val(void) { return t_value; } }; inline void c_test::fault(bool hard) { t_value |= (hard?TEST1:TEST2); } a.cpp ---------------------- #include <stdlib.h> #include <stdio.h> #include "a.h" int main(int argc, const char **argv) { c_test t; t.fault(argc == 2); printf("%llz\n", t.val()); return 0; } When compiled without -O: $ g++ -o a /tmp/a.o /tmp/a.o: In function `c_test::fault(bool)': a.cpp:(.text._ZN6c_test5faultEb[c_test::fault(bool)]+0x1d): undefined reference to `c_test::TEST1' a.cpp:(.text._ZN6c_test5faultEb[c_test::fault(bool)]+0x26): undefined reference to `c_test::TEST2' collect2: ld returned 1 exit status $ cat /tmp/a.s .file "a.cpp" .section .text._ZN6c_testC2Ev,"axG",@progbits,_ZN6c_testC5Ev,comdat .align 2 .weak _ZN6c_testC2Ev .type _ZN6c_testC2Ev, @function _ZN6c_testC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq $0, (%rax) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN6c_testC2Ev, .-_ZN6c_testC2Ev .weak _ZN6c_testC1Ev .set _ZN6c_testC1Ev,_ZN6c_testC2Ev .section .text._ZN6c_test3valEv,"axG",@progbits,_ZN6c_test3valEv,comdat .align 2 .weak _ZN6c_test3valEv .type _ZN6c_test3valEv, @function _ZN6c_test3valEv: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq (%rax), %rax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size _ZN6c_test3valEv, .-_ZN6c_test3valEv .section .text._ZN6c_test5faultEb,"axG",@progbits,_ZN6c_test5faultEb,comdat .align 2 .weak _ZN6c_test5faultEb .type _ZN6c_test5faultEb, @function _ZN6c_test5faultEb: .LFB4: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) movl %esi, %eax movb %al, -12(%rbp) movq -8(%rbp), %rax movq (%rax), %rdx cmpb $0, -12(%rbp) je .L5 movq _ZN6c_test5TEST1E(%rip), %rax jmp .L6 .L5: movq _ZN6c_test5TEST2E(%rip), %rax .L6: orq %rax, %rdx movq -8(%rbp), %rax movq %rdx, (%rax) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE4: .size _ZN6c_test5faultEb, .-_ZN6c_test5faultEb .section .rodata .LC0: .string "%llz\n" .text .globl main .type main, @function main: .LFB5: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 .cfi_lsda 0x3,.LLSDA5 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $32, %rsp movl %edi, -20(%rbp) movq %rsi, -32(%rbp) leaq -16(%rbp), %rax movq %rax, %rdi call _ZN6c_testC1Ev cmpl $2, -20(%rbp) sete %al movzbl %al, %edx leaq -16(%rbp), %rax movl %edx, %esi movq %rax, %rdi call _ZN6c_test5faultEb leaq -16(%rbp), %rax movq %rax, %rdi call _ZN6c_test3valEv movq %rax, %rsi movl $.LC0, %edi movl $0, %eax .LEHB0: call printf .LEHE0: movl $0, %eax jmp .L11 .L10: movq %rax, %rdi .LEHB1: call _Unwind_Resume .LEHE1: .L11: leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE5: .globl __gxx_personality_v0 .section .gcc_except_table,"a",@progbits .LLSDA5: .byte 0xff .byte 0xff .byte 0x1 .uleb128 .LLSDACSE5-.LLSDACSB5 .LLSDACSB5: .uleb128 .LEHB0-.LFB5 .uleb128 .LEHE0-.LEHB0 .uleb128 .L10-.LFB5 .uleb128 0 .uleb128 .LEHB1-.LFB5 .uleb128 .LEHE1-.LEHB1 .uleb128 0 .uleb128 0 .LLSDACSE5: .text .size main, .-main .ident "GCC: (GNU) 4.7.0 20120301 (experimental)" .section .note.GNU-stack,"",@progbits When compiled with -O1: ---------------------- .file "a.cpp" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "%llz\n" .text .globl main .type main, @function main: .LFB24: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 cmpl $2, %edi movabsq $-9223372036854775808, %rax movabsq $4611686018427387904, %rsi cmove %rax, %rsi movl $.LC0, %edi movl $0, %eax call printf movl $0, %eax addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE24: .size main, .-main .ident "GCC: (GNU) 4.7.0 20120301 (experimental)" .section .note.GNU-stack,"",@progbits /tmp/a.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: 0: 48 83 ec 08 sub $0x8,%rsp 4: 83 ff 02 cmp $0x2,%edi 7: 48 b8 00 00 00 00 00 mov $0x8000000000000000,%rax e: 00 00 80 11: 48 be 00 00 00 00 00 mov $0x4000000000000000,%rsi 18: 00 00 40 1b: 48 0f 44 f0 cmove %rax,%rsi 1f: bf 00 00 00 00 mov $0x0,%edi 24: b8 00 00 00 00 mov $0x0,%eax 29: e8 00 00 00 00 callq 2e <main+0x2e> 2e: b8 00 00 00 00 mov $0x0,%eax 33: 48 83 c4 08 add $0x8,%rsp 37: c3 retq This version of c_test::fault will generate the inline literal correctly for both cases: inline void c_test::fault(bool hard) { if (hard) { t_value |= TEST1; } else { t_value |= TEST2; } } I surmise that there is something funny in the front-end vis-a-vis handling of the '?:' operators. I seem to recall working around this in some 3-series compilers (fedora core 4 days) as well.