--- Begin Message ---
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
--- End Message ---