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;
}

Reply via email to