Package: gcc-4.6 Version: 4.6.3-1 Severity: normal Dear Maintainer,
I have a program that loops over const char * pointers in named sections. In this loop, incorrect generated assembly code causes wrong output, e.g. it outputs (null) instead of the string pointed to by the const char * pointer. This (null) can be tracked down to the generated assembly code where it puts a static zero on the stack instead of the address of the string. This incorrect code is generated for 32 bit with optimization levels 1-3 only. 64 bit is not affected, neither is unoptimized 32 bit. I found various versions of gcc 4.6.x containing this bug, including: TDM-GCC 4.6.1, MinGW gcc 4.6.2 and Debian gcc 4.6.3-1 so it seems to me it is a 4.6 issue. I can confirm that neither MinGW gcc 4.5.2 nor Debian gcc 4.4.5 (Debian Squeeze) contain this bug. I already filed a bug against TDM-GCC 4.6.1: http://sourceforge.net/tracker/?func=detail&aid=3532366&group_id=200665&atid=974439 Quoting from this bug report: ===== quote begin ===== The expected and correct output is: B: Handling call with 1 args B: Handling call one B: Handling call with 1 args B: Handling call with 3 args B: Handling call three B: Handling call with 3 args B: Handling call with 2 args B: Handling call two B: Handling call with 2 args The incorrect output is: B: Handling call with 1 args B: Handling call (null) B: Handling call with 0 args B: Handling call with 3 args B: Handling call (null) B: Handling call with 0 args B: Handling call with 2 args B: Handling call (null) B: Handling call with 0 args The issue can be tracked down to the generated assembly code where the code puts a static zero on the stack for printf (instead of the address of the char array), resulting in the "(null)" output. Same goes for the "0 args" output. The incorrect assembly code can be found in lines 30-35 of the attached s file: movl $0, 4(%esp) movl $LC1, (%esp) call _printf movl $0, 4(%esp) movl $LC0, (%esp) call _printf ===== quote end ===== I'll attach the test program and additional files. Please note that I had to make slightly modifications to the section names and linker options in order to get gcc 4.6.3-1 (or its ld) to sort the sections in alphabetic order (compared to the test program that I filed with the TDM-GCC bug). thank you for looking into this issue, Chris -- System Information: Debian Release: wheezy/sid APT prefers testing APT policy: (500, 'testing') Architecture: amd64 (x86_64) Kernel: Linux 3.2.0-2-amd64 (SMP w/2 CPU cores) Locale: LANG=en_CA.utf8, LC_CTYPE=en_CA.utf8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Versions of packages gcc-4.6 depends on: ii binutils 2.22-6.1 ii cpp-4.6 4.6.3-1 ii gcc-4.6-base 4.6.3-1 ii libc6 2.13-32 ii libgcc1 1:4.7.0-8 ii libgmp10 2:5.0.5+dfsg-1.1 ii libgomp1 4.7.0-8 ii libmpc2 0.9-4 ii libmpfr4 3.1.0-5 ii libquadmath0 4.7.0-8 ii zlib1g 1:1.2.7.dfsg-11 Versions of packages gcc-4.6 recommends: ii libc6-dev 2.13-32 Versions of packages gcc-4.6 suggests: pn binutils-gold <none> pn gcc-4.6-doc <none> pn gcc-4.6-locales <none> pn gcc-4.6-multilib 4.6.3-1 pn libgcc1-dbg <none> pn libgomp1-dbg <none> pn libmudflap0-4.6-dev <none> pn libmudflap0-dbg <none> pn libquadmath0-dbg <none> -- no debconf information
/* This program reveals a bug in: * - TDM-GCC 4.6.1 (tdm64-1) * - MinGW gcc 4.6.2 (GCC) * - Debian Wheezy amd64 gcc 4.6.3 (Debian 4.6.3-1) * in three optimization levels for 32 bit. * 64 bit is not affected. In detail: * * The function "broken" shows results of the incorrect code. * The function "working" works on all optimization levels and * bitnesses. * The only difference between those functions is that "broken" uses * a local variable for array access while "working" uses a global one. * * broken: * gcc -m32 -Wall -Werror -O1 -Wl,--sort-section=name -o tdmbug tdmbug.c * gcc -m32 -Wall -Werror -O2 -Wl,--sort-section=name -o tdmbug tdmbug.c * gcc -m32 -Wall -Werror -O3 -Wl,--sort-section=name -o tdmbug tdmbug.c * * working: * gcc -m32 -Wall -Werror -O0 -Wl,--sort-section=name -o tdmbug tdmbug.c * gcc -m64 -Wall -Werror -O0 -Wl,--sort-section=name -o tdmbug tdmbug.c * gcc -m64 -Wall -Werror -O1 -Wl,--sort-section=name -o tdmbug tdmbug.c * gcc -m64 -Wall -Werror -O2 -Wl,--sort-section=name -o tdmbug tdmbug.c * gcc -m64 -Wall -Werror -O3 -Wl,--sort-section=name -o tdmbug tdmbug.c */ #include <stdio.h> __attribute__((section(".rodata.SysWrap_nargs_aaa"))) const int na_aaa = 0; __attribute__((section(".rodata.SysWrap_nargs_one"))) const int na_one = 1; __attribute__((section(".rodata.SysWrap_nargs_two"))) const int na_two = 2; __attribute__((section(".rodata.SysWrap_nargs_three"))) const int na_three = 3; __attribute__((section(".rodata.SysWrap_nargs_zzz"))) const int na_zzz = 0; __attribute__((section(".rodata.SysWrap_before_names_aaa"))) const char *const bn_aaa = NULL; __attribute__((section(".rodata.SysWrap_before_names_one"))) const char *const bn_one = "one"; __attribute__((section(".rodata.SysWrap_before_names_two"))) const char *const bn_two = "two"; __attribute__((section(".rodata.SysWrap_before_names_three"))) const char *const bn_three = "three"; __attribute__((section(".rodata.SysWrap_before_names_zzz"))) const char *const bn_zzz = NULL; int g_num_names; void broken(void) { const char * const * names; int i; g_num_names = 0; for (i = 0, names = &bn_aaa + 1; names < &bn_zzz; i++, names++) { g_num_names++; printf("B: Handling call with %d args\n", *(&na_aaa + 1 + i)); printf("B: Handling call %s\n", *names); printf("B: Handling call with %d args\n", *(&na_aaa + 1 + i)); } } void working(void) { const char * const * names; int i; g_num_names = 0; for (i = 0, names = &bn_aaa + 1; names < &bn_zzz; i++, names++) { g_num_names++; printf("W: Handling call with %d args\n", *(&na_aaa + g_num_names)); printf("W: Handling call %s\n", *names); printf("W: Handling call with %d args\n", *(&na_aaa + g_num_names)); } } int main(void) { working(); broken(); return 0; }
.file "tdmbug.c" .section .rodata.str1.4,"aMS",@progbits,1 .align 4 ..LC0: .string "B: Handling call with %d args\n" .section .rodata.str1.1,"aMS",@progbits,1 ..LC1: .string "B: Handling call %s\n" .text .globl broken .type broken, @function broken: ..LFB11: .cfi_startproc pushl %esi .cfi_def_cfa_offset 8 .cfi_offset 6, -8 pushl %ebx .cfi_def_cfa_offset 12 .cfi_offset 3, -12 subl $20, %esp .cfi_def_cfa_offset 32 movl $0, g_num_names movl $bn_aaa+4, %eax cmpl $bn_zzz, %eax jae .L1 movl $bn_zzz+3, %esi subl $bn_aaa+8, %esi shrl $2, %esi addl $1, %esi movl $0, %ebx ..L3: addl $1, g_num_names movl na_aaa+4(,%ebx,4), %eax movl %eax, 4(%esp) movl $.LC0, (%esp) call printf movl $0, 4(%esp) movl $.LC1, (%esp) call printf movl $0, 4(%esp) movl $.LC0, (%esp) call printf addl $1, %ebx cmpl %esi, %ebx jne .L3 ..L1: addl $20, %esp .cfi_def_cfa_offset 12 popl %ebx .cfi_def_cfa_offset 8 .cfi_restore 3 popl %esi .cfi_def_cfa_offset 4 .cfi_restore 6 ret .cfi_endproc ..LFE11: .size broken, .-broken .section .rodata.str1.4 .align 4 ..LC2: .string "W: Handling call with %d args\n" .section .rodata.str1.1 ..LC3: .string "W: Handling call %s\n" .text .globl working .type working, @function working: ..LFB12: .cfi_startproc pushl %ebx .cfi_def_cfa_offset 8 .cfi_offset 3, -8 subl $24, %esp .cfi_def_cfa_offset 32 movl $0, g_num_names movl $bn_aaa+4, %eax cmpl $bn_zzz, %eax jae .L5 movl %eax, %ebx ..L7: movl g_num_names, %eax addl $1, %eax movl %eax, g_num_names movl na_aaa(,%eax,4), %eax movl %eax, 4(%esp) movl $.LC2, (%esp) call printf movl (%ebx), %eax movl %eax, 4(%esp) movl $.LC3, (%esp) call printf movl g_num_names, %eax movl na_aaa(,%eax,4), %eax movl %eax, 4(%esp) movl $.LC2, (%esp) call printf addl $4, %ebx cmpl $bn_zzz, %ebx jb .L7 ..L5: addl $24, %esp .cfi_def_cfa_offset 8 popl %ebx .cfi_def_cfa_offset 4 .cfi_restore 3 ret .cfi_endproc ..LFE12: .size working, .-working .globl main .type main, @function main: ..LFB13: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp call working call broken movl $0, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc ..LFE13: .size main, .-main .comm g_num_names,4,4 .globl bn_zzz .section .rodata.SysWrap_before_names_zzz,"a",@progbits .align 4 .type bn_zzz, @object .size bn_zzz, 4 bn_zzz: .zero 4 .globl bn_three .section .rodata.str1.1 ..LC4: .string "three" .section .rodata.SysWrap_before_names_three,"a",@progbits .align 4 .type bn_three, @object .size bn_three, 4 bn_three: .long .LC4 .globl bn_two .section .rodata.str1.1 ..LC5: .string "two" .section .rodata.SysWrap_before_names_two,"a",@progbits .align 4 .type bn_two, @object .size bn_two, 4 bn_two: .long .LC5 .globl bn_one .section .rodata.str1.1 ..LC6: .string "one" .section .rodata.SysWrap_before_names_one,"a",@progbits .align 4 .type bn_one, @object .size bn_one, 4 bn_one: .long .LC6 .globl bn_aaa .section .rodata.SysWrap_before_names_aaa,"a",@progbits .align 4 .type bn_aaa, @object .size bn_aaa, 4 bn_aaa: .zero 4 .globl na_zzz .section .rodata.SysWrap_nargs_zzz,"a",@progbits .align 4 .type na_zzz, @object .size na_zzz, 4 na_zzz: .zero 4 .globl na_three .section .rodata.SysWrap_nargs_three,"a",@progbits .align 4 .type na_three, @object .size na_three, 4 na_three: .long 3 .globl na_two .section .rodata.SysWrap_nargs_two,"a",@progbits .align 4 .type na_two, @object .size na_two, 4 na_two: .long 2 .globl na_one .section .rodata.SysWrap_nargs_one,"a",@progbits .align 4 .type na_one, @object .size na_one, 4 na_one: .long 1 .globl na_aaa .section .rodata.SysWrap_nargs_aaa,"a",@progbits .align 4 .type na_aaa, @object .size na_aaa, 4 na_aaa: .zero 4 .ident "GCC: (Debian 4.6.3-1) 4.6.3" .section .note.GNU-stack,"",@progbits