Bug 4029 - relax_segment can't stabilize .gcc_except_table
Summary: relax_segment can't stabilize .gcc_except_table
Alias: None
Product: binutils
Classification: Unclassified
Component: gas (show other bugs)
Version: 2.18
: P2 normal
Target Milestone: ---
Assignee: Alan Modra
Depends on:
Reported: 2007-02-12 16:31 UTC by Andreas Schwab
Modified: 2018-03-31 12:43 UTC (History)
4 users (show)

See Also:
Target: x86-64-*-*
Last reconfirmed:

Testcase (229.14 KB, application/x-bzip2)
2007-02-12 16:32 UTC, Andreas Schwab
A kludge (618 bytes, patch)
2007-02-18 13:00 UTC, H.J. Lu
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
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]
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

Comment 5 Alan Modra 2007-03-10 10:58:01 UTC
Reduced testcase.

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

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.

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

 .align 4
 .byte 0, 0
 .uleb128 end - start
 .space 128*128 - 1 /* or -2 or -3 */
 .align 8
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


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

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
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.