Bug 4029

Summary: relax_segment can't stabilize .gcc_except_table
Product: binutils Reporter: Andreas Schwab <schwab>
Component: gasAssignee: Alan Modra <amodra>
Status: RESOLVED FIXED    
Severity: normal CC: bug-binutils, hjl.tools, jeremip11, rprichard
Priority: P2    
Version: 2.18   
Target Milestone: ---   
Host: Target: x86-64-*-*
Build: Last reconfirmed:
Attachments: Testcase
A kludge

Description Andreas Schwab 2007-02-12 16:31:34 UTC
relax_segment loops forever on relaxing .gcc_except_table, with growth 
oscillating between 1 and -1.
Comment 1 Andreas Schwab 2007-02-12 16:32:57 UTC
Created attachment 1549 [details]
Testcase
Comment 2 H.J. Lu 2007-02-18 07:05:18 UTC
The problem is with rs_leb128 processing. When size needed for
the new value is 0x3fff is 2 and the old size is 3, growth is -1. Then
the next time the new value becomes 0x4000 and its size becomes 3. That
is an infinite loop.
Comment 3 H.J. Lu 2007-02-18 13:00:01 UTC
Created attachment 1564 [details]
A kludge

This kludge compiles the testcase. But I am not sure if it is correct.
Comment 4 H.J. Lu 2007-02-19 02:56:51 UTC
A patch is posted at

http://sourceware.org/ml/binutils/2007-02/msg00280.html
Comment 5 Alan Modra 2007-03-10 10:58:01 UTC
Reduced testcase.

 .data
 .align 4
 .byte 0, 0
 .uleb128 end - start
start:
 .space 128*128 - 1 /* or -2 or -3 */
 .align 4
end:

I don't believe this is a gas bug.  Instead, I believe gcc is asking the
impossible of gas.  The original testcase or the reduced one above has no
correct solution.  ie. this problem is caused by a design fault in the layout of
.gcc_except_table data.

Consider:
We know the uleb128 is either two or three bytes long.  If end-start is
128*128-1 or somewhat less, then it will be two bytes.  If 128*128 or a little
larger, then it will be three bytes.

But, if the uleb128 is two bytes in size, then end-start is 128*128
If the uleb128 is three bytes, then end-start is 128*128-1
This results in a contradiction.
Comment 6 H.J. Lu 2007-03-10 15:20:32 UTC
Assembler can do better to help gcc. We can turn it into

 .data
 .align 4
 .byte 0, 0
 .uleb128 end - start
start:
 .space 128*128 - 1 /* or -2 or -3 */
 .align 8
end:
Comment 7 Alan Modra 2007-03-11 13:37:03 UTC
Yes, we can modify the output, but we must do so in a way that does not confuse
consumers of .gcc_except_table.  I don't believe changing the .align is correct,
because data normally follows the .align (a number of .long's before the end
label).  Changing alignment in a way that cures the uleb128 problem necessarily
inserts zero bytes, which I think will confuse the unwinder.
Comment 8 H.J. Lu 2007-03-11 22:00:15 UTC
The assembly code in question is

        .align 4
        .long   DW.ref._ZTIN10DSEBuiltin17ExceptionDeadLockE-.
        .long   DW.ref._ZTI7DSEBomb-.
        .long   DW.ref._ZTI12DSEException-.
        .long   DW.ref._ZTISt9exception-.
        .long   0

.LLSDATT5533:

If those data are accessed via .LLSDATT5533 as base, it is s safe to change
.align 4 to .align 8. I think .align 8 is compatible with .align 4. If
it isn't true, it is a bug in assembly code.

Andreas, can you upload the preprocessed C++ source and assembly code
generated with -dA?
Comment 9 H.J. Lu 2007-03-12 00:52:44 UTC
A patch is posted at

http://sourceware.org/ml/binutils/2007-03/msg00107.html
Comment 10 Alan Modra 2007-03-12 09:01:15 UTC
Ah, yes, it does seem as if the type_info table is stored in reverse order and
accessed from the end.  In that case increasing alignment padding should work.
Comment 12 Alan Modra 2007-03-14 11:07:55 UTC
Fixed
Comment 13 Ryan Prichard 2018-01-12 23:54:35 UTC
FWIW: This issue affects the LLVM assembler, too: https://bugs.llvm.org/show_bug.cgi?id=35809.