bug-gnu-utils
[Top][All Lists]
Advanced

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

bug in gas from gcc code


From: James L Peterson
Subject: bug in gas from gcc code
Date: Fri, 02 Nov 2001 12:05:33 -0600

We are getting the following error from g++:

/tmp/ccK9l1qf.s: Assembler messages:
/tmp/ccK9l1qf.s:2991: Error: invalid section for operation
/tmp/ccK9l1qf.s:2992: Error: invalid section for operation

Looking at the assembly language generated by gcc,
we get:

   2985         .section        .rodata
   2986         .align 16
   2987         .align 4
   2988 .L132:
   2989         .long .L96-.+4+(.-.L132)
   2990         .long .L105-.+4+(.-.L132)
   2991         .long .L127-.+4+(.-.L132)
   2992         .long .L130-.+4+(.-.L132)
   2993         .section .text
       ... eventually here we have .L96:  .L105:, .L127: and .L130:
defined in .text section


This code is a jump table for a switch statement.  The addresses
L96, L105, L127, L130 are the cases for the switch.  A first
question is why the first two do not generate an error, if
the last two do.

Checking what is happening, in the assembler, we do expression
processing for the right hand side.  For the last two, we end
up with an expression which is a tree:

   op_add
        op_subtract  + 4
                L127
                .
       op_subtract
                .
               L132

(The expressions have a separate field that keeps track
of any plus/minus constant values, X_add_number)

The error results from the first subtract thinking the
first operand L127 is in ".text", while the second is
in section ".rodata".  The assembler can't do a subtract
across different sections.

Why don't we get the same treatment for the first two?
Well, they are handled differently.  The assembler
figures out that (.-L132) is just a number, and so
the expression becomes:

        op_subtract  + 4 + (.-L132)
                L127
                .

Somehow when the expression is of this form, the code
doesn't check the sections, and we don't get an error.

The optimization of (.-L132) is controlled by the following
in expr.c:

      /* This case comes up in PIC code.  */
      else if (op_left == O_subtract
        && right.X_op == O_symbol
        && resultP->X_op == O_symbol
        && (symbol_get_frag (right.X_add_symbol)
     == symbol_get_frag (resultP->X_add_symbol))
        && SEG_NORMAL (rightseg))
 {
   resultP->X_add_number -= right.X_add_number;
   resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol)
        - S_GET_VALUE (right.X_add_symbol));
   resultP->X_op = O_constant;
   resultP->X_add_symbol = 0;
 }

For the first two lines, eveything works, and we take the
optimization.  For the third line (and then for the 4th),
we have different fragments; the symbol_get_frag
for the right and the resultP are not the same.  Presumably
the reason we care about the fragments being the same is
that if the two symbols are in different fragments, they are
in different segments, and so can't be just subtracted.

Why are the fragments different?
It appears that after the first two lines of the switch table,
we switch to a new fragment because the old one fills up.  The
stack trace at that point is:

#0  frag_new (old_frags_var_max_size=0) at
../../binutils/gas/frags.c:135
#1  0x0805461f in frag_grow (nchars=4) at ../../binutils/gas/frags.c:75
#2  0x080549aa in frag_more (nchars=4) at ../../binutils/gas/frags.c:180

#3  0x0805f947 in emit_expr (exp=0xbffff6c0, nbytes=4)
    at ../../binutils/gas/read.c:3549

We are trying to grow the fragment by 4 bytes, and

  if (obstack_room (&frchain_now->frch_obstack) < nchars)

is true.  obstack_room is a macro and  we check
(__o->chunk_limit - __o->next_free).  At this point,

(gdb) print frchain_now->frch_obstack
$1 = {chunk_size = 4072, chunk = 0x8148928, object_base = 0x814990c "",
  next_free = 0x8149910 "", chunk_limit = 0x8149910 "", temp = 0,
  alignment_mask = 3, chunkfun = 0x80cd9a0 <xmalloc>,
  freefun = 0x8049510 <free>, extra_arg = 0x0, use_extra_arg = 0,
  maybe_empty_object = 1, alloc_failed = 0}

we see that chunk_limit = 0x8149910 = next_free, so there is
no room.  We close off this fragment, and start a new one.
Then when we check for fragment equality, it fails, so we
don't do the (.-L132) optimization, so we build a complex tree
for the expression, which generates an error.

This raises several questions:

1. Why do we close the fragment and start a new one?
The fragments are built on the obstack data type, and it
has (in obstack.h) operations of

void obstack_grow (struct obstack *obstack, void *data, int size);
void obstack_make_room (struct obstack *obstack, int size);

which would seem to match what we are trying to do with frag_more
and frag_grow.  If we were to use the underlying obstack_grow
and obstack_make_room functions, we might be able to avoid having
to have so many different fragments.

2. Why do we check if the fragments are the same, and not the
sections, in expr.c, to decide if we can compute the difference
in the assembler?  Since fragments may change at any time, the
important thing would be that the two symbols are defined and
in the same segment.  If this is true, then the difference
between the symbols is just a fixed offset.

1753,1754c1750,1751
<         && (S_GET_SEGMENT(right.X_add_symbol)
<      == S_GET_SEGMENT (resultP->X_add_symbol))
---
>         && (symbol_get_frag (right.X_add_symbol)
>      == symbol_get_frag (resultP->X_add_symbol))


3. Should we be generating these "invalid" segment error messages,
and if so, why don't we do so for all 4 table entries?  And if
it is an error -- how do we get gcc to generate correct code?


I'm running on a Linux system with GNU assembler 2.11.92
The file /tmp/ccK9l1qf.s, generated by gcc, is attached.
This is for an X86-64 assembly (the 64bit extension to x86
from AMD).  I invoke gas with no parameters:

   /home/peterson/work/tc6/opt/x86-64/bin/x86_64-unknown-linux-as
/tmp/ccK9l1qf.s

after compiling the binutils tools for the x86-64:

     HERE=`pwd`
    cd binutils-build
    ../binutils/configure \
            --target=x86_64-unknown-linux \
            --prefix=$HERE/opt/x86-64
    make
    make install






reply via email to

[Prev in Thread] Current Thread [Next in Thread]