[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#648016: Some analysis

reassign 648016 gcc-4.6
found 648016 4.6.2-4

This bug really is in gcc-4.6, because it is currently the default sid 
gcc and it is used to (mis)compile src/build/genrecog.c during 
gcc-avr build, which later crashes. I'm fairly certain that this is 
gcc problem, because if the binary is compiled with -O0, the problem 
goes away. All debugging output below was obtained on a sparc machine 
running up-to-date sid, invoking build/genrecog under gdb with a 
single argument of '../../src/gcc/config/avr/avr.md'.

Tracing the execution is somewhat tricky, since failure happens within 
write_tree(), and most of the functions write_tree() calls 
(write_tree_1, write_switch, write_node, write_action, etc) are
optimized out. The output generated by 

build/genrecog ../../src/gcc/config/avr/avr.md

is the same as the one produced on an amd64 system until we hit the 
following code in genrecog.c/write_switch(): 

  else if (type == DT_mode
       || type == DT_veclen
       || type == DT_elt_zero_int
       || type == DT_elt_one_int
       || type == DT_elt_zero_wide_safe)
      const char *indent = "";

      /* We cast switch parameter to integer, so we must ensure that the value
     fits.  */
      if (type == DT_elt_zero_wide_safe)
      indent = "  ";
      printf("  if ((int) XWINT (x%d, 0) == XWINT (x%d, 0))\n", depth, depth);
      printf ("%s  switch (", indent);
      switch (type)
    case DT_mode:
      printf ("GET_MODE (x%d)", depth);
    case DT_veclen:
      printf ("XVECLEN (x%d, 0)", depth);
    case DT_elt_zero_int:
      printf ("XINT (x%d, 0)", depth);
    case DT_elt_one_int:
      printf ("XINT (x%d, 1)", depth);
    case DT_elt_zero_wide_safe:
      /* Convert result of XWINT to int for portability since some C
         compilers won't do it and some will.  */
      printf ("(int) XWINT (x%d, 0)", depth);
      gcc_unreachable ();

The problem appears after executing the 

printf ("%s  switch (", indent);

statetement. It looks like compiler generates a number of small stubs 
within write_tree() for calling printf with all possible format 
statements. Here's how the generated assembler code looks for this 
particular one, starting at 0x00013e60:

Dump of assembler code from 0x13e40 to 0x13ea0:
   0x00013e40 <write_tree+2144>:	ld  [ %i5 + 0x1c ], %o2
   0x00013e44 <write_tree+2148>:	sethi  %hi(0x1e800), %o0
   0x00013e48 <write_tree+2152>:	or  %l1, 0x110, %o1
   0x00013e4c <write_tree+2156>:	call  0x3510c <printf@plt>
   0x00013e50 <write_tree+2160>:	or  %o0, 0x258, %o0
   0x00013e54 <write_tree+2164>:	b  %xcc, 0x13bf4 <write_tree+1556>
   0x00013e58 <write_tree+2168>:	ld  [ %i0 ], %i5
   0x00013e5c <write_tree+2172>:	be,pn   %icc, 0x13850 <write_tree+624>
=> 0x00013e60 <write_tree+2176>:	sethi  %hi(0x1f400), %i3
   0x00013e64 <write_tree+2180>:	sethi  %hi(0x1e800), %o0
   0x00013e68 <write_tree+2184>:	or  %o0, 0x2b0, %o0	! 0x1eab0
   0x00013e6c <write_tree+2188>:	call  0x3510c <printf@plt>
   0x00013e70 <write_tree+2192>:	or  %i3, 0xe8, %o1
   0x00013e74 <write_tree+2196>:	cmp  %l7, 7
   0x00013e78 <write_tree+2200>:	sll  %l7, 2, %g1
   0x00013e7c <write_tree+2204>:	sethi  %hi(0x1e800), %o0
   0x00013e80 <write_tree+2208>:	mov  %l6, %o1
   0x00013e84 <write_tree+2212>:	call  0x3510c <printf@plt>
   0x00013e88 <write_tree+2216>:	or  %o0, 0x3e8, %o0
   0x00013e8c <write_tree+2220>:	b  %xcc, 0x13bac <write_tree+1484>
   0x00013e90 <write_tree+2224>:	ld  [ %fp + -192 ], %g3
   0x00013e94 <write_tree+2228>:	b  %xcc, 0x13ad0 <write_tree+1264>
   0x00013e98 <write_tree+2232>:	st  %g2, [ %fp + -188 ]
   0x00013e9c <write_tree+2236>:	cmp  %g0, %i3
End of assembler dump.

Confirmation that 0x1eab0 contains the correct format statement 
(passed to printf in %o0):

(gdb) printf "%s\n", (char *) 0x1eab0
%s  switch (

A remarkable feature of this stub is that it does not have a return 
branch statement, like others do (see 0x00013e54, for example). So, 
instead of returning to the correct location where the stub was 
invoked in write_switch(), we fall through to 0x00013e74, and start 
executing the next stub, which invokes printf with a format 
statement at 0x1ebe8 (== 0x1e800 | 0x3e8):

(gdb) printf "%s\n", (char *) 0x1ebe8
%sreturn gen_split_%d (insn, operands);


This is completely unrelated code, normally invoked by 
write_action(), line 2182. Once it's done, we jump back to completely 
wrong location at 0x00013e8c, eventually causing a crash.

Best regards,
Jurij Smakov                                           jurij@wooyd.org
Key: http://www.wooyd.org/pgpkey/                      KeyID: C99E03CC

Reply to: