On Tue, Jun 19, 2012 at 10:54 AM, Richard Guenther <richard.guent...@gmail.com> wrote: > The issue is that your testcase is invalid. > int x = ret(*(&fooS + i)); > this access is only ever valid for i == 0 as otherwise you are creating > a pointer that points outside of the object fooS.
Richard, thanks for your reply. The testcase is invalid also for other reasons, a big one being the automatic sorting and merging of sections with a dollar sign in their names is a Windows-originated extension used for PE target only, which makes it not work elsewhere. Sorry about that, I'll refrain from using anything non-standard here. Accessing outside object bounds is IMO a common C practice allowed by the existence of pointers. This exact technique is used for decentralized lists created during compile-time, be it extensible handler/hook structures, pointers to init/fini functions etc. It has notable use e.g. in Linux kernel [1], [2]. The programmer places defined data to a special linker section in individual compilation units, then traverse through it using linker-provided symbols (e.g. ld creates __start_<section-name> and __end_<section-name> automatically), as test0.c shows: $ gcc -O1 -m32 -fno-toplevel-reorder test0.c && ./a.out 0: 1 1: 2 2: 3 The sole reason for messing with the section attributes is to keep the values together. Because I can force the order (to the necessary extent) by -fno-toplevel-reorder, the program can be changed to use just bounding variables without any linker magic (test1.c): $ gcc -O1 -m32 -fno-toplevel-reorder test1.c && ./a.out 0: 1 1: 2 2: 3 The only changes in the code are removing the section attributes and adding offset by one, skipping the starting element (as __start_foo has a size now). Now, changing the end condition from test for the end address to test for the end sentinel -1 and duplicating the printf() line (to hit the right optimization spot), something weird happens (test2.c): $ gcc -O1 -m32 -fno-toplevel-reorder test2.c && ./a.out 0: 1 0: -1 1: 2 1: -1 2: 3 2: -1 Why is the second line in each iteration different from the first? It should be printing exactly the same expression. Analyzing the dom phase log shows the memory access is optimized to constant value of the base variable, hence -1. And without optimization, both of them are correct: $ gcc -O0 -m32 test2.c && ./a.out 0: 1 0: 1 1: 2 1: 2 2: 3 2: 3 That is the problem I am talking about and which the patch aims to address. Jiri [1] http://www.compsoc.man.ac.uk/~moz/kernelnewbies/documents/initcall/index.html [2] http://lkml.indiana.edu/hypermail/linux/kernel/0706.2/2552.html
#include <stdio.h> __attribute__((section("foo"))) const int foo1 = 1; __attribute__((section("foo"))) const int foo2 = 2; __attribute__((section("foo"))) const int foo3 = 3; extern const int __start_foo, __stop_foo; int main(void) { int i; i = 0; do { printf("%d: %d\n", i, *(&__start_foo + i)); i++; } while(&__start_foo + i != &__stop_foo); return 0; }
#include <stdio.h> const int __start_foo = -1; const int foo1 = 1; const int foo2 = 2; const int foo3 = 3; const int __stop_foo = -1; int main(void) { int i; i = 0; do { printf("%d: %d\n", i, *(&__start_foo + 1 + i)); i++; } while(&__start_foo + 1 + i != &__stop_foo); return 0; }
#include <stdio.h> const int __start_foo = -1; const int foo1 = 1; const int foo2 = 2; const int foo3 = 3; const int __stop_foo = -1; int main(void) { int i; i = 0; do { printf("%d: %d\n", i, *(&__start_foo + 1 + i)); printf("%d: %d\n", i, *(&__start_foo + 1 + i)); i++; } while(*(&__start_foo + 1 + i) != -1); return 0; }