Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 8 Nov 2018 21:01:03 -0800
From:      Mark Millard <marklmi@yahoo.com>
To:        FreeBSD Toolchain <freebsd-toolchain@freebsd.org>, FreeBSD PowerPC ML <freebsd-ppc@freebsd.org>
Subject:   gnu/lib/libgcc 's unwind-dw2.c and company: DW_CFA_{remember,restore}_state handling is incomplete and gcc 4.2.1 output tends to avoid those (powerpc examples)
Message-ID:  <B2566AE9-C9A7-449A-83C2-5FB942EC3E52@yahoo.com>

next in thread | raw e-mail | index | archive | help
Some context:

/usr/src/gnu/lib/libgcc/Makefile has:

.if ${TARGET_CPUARCH} =3D=3D "arm"
LIB2ADDEH =3D     unwind-arm.c libunwind-arm.S pr-support.c unwind-c.c
.else
LIB2ADDEH =3D unwind-dw2.c unwind-dw2-fde-glibc.c unwind-sjlj.c =
gthr-gnat.c \
        unwind-c.c
.endif

It appears that only powerpc families and sparc64 use
unwind-dw2.c and company unless someone uses
WITHOUT_LLVM_LIBUNWIND=3D deliberately for non-arm. This
is about unwind-dw2.c and company and uses a powerpc
context for illustration.


The problem:

gnu/lib/libgcc 's libgcc_s.so when based on unwind-dw2.c and
company do not correctly/completely handle:

DW_CFA_remember_state
DW_CFA_restore_state

as they are put to use by fairly modern c++ compilers,
use that requires cfa-rule save/restore.

Here I use g++8 for illustration, but g++6 is similar,
for example. Yet gcc 4.2.1 tends to "work" because it
has less complete stack (cfa) tracking in the exception
handling output it produces, thus commonly avoiding use
of DW_CFA_{remember,restore}_state .


The evidence:

I'm going to illustrate with 32-powerpc because I happen
to have g++ 4.2.1 handy in that context, as well as g++8 .


g++ 4.2.1 toolchain (and why it does not illustrate the problem):
(The c++ source code is not directly significant here --but
that it is the same for both g++*'s is important .)

(gdb) disass g
Dump of assembler code for function g():
(mixed with related dwarfdump lines and other notes)

        0x018008e0: <off cfa=3D00(r1) >=20
   0x018008e0 <+0>:	mflr    r0
   0x018008e4 <+4>:	stwu    r1,-16(r1)

        0x018008e8: <off cfa=3D16(r1) >=20
   0x018008e8 <+8>:	lis     r9,385
   0x018008ec <+12>:	stw     r0,20(r1)

        0x018008f0: <off cfa=3D16(r1) > <off r65=3D04(cfa) >=20
   0x018008f0 <+16>:	lwz     r0,3496(r9)
   0x018008f4 <+20>:	cmpwi   cr7,r0,0
   0x018008f8 <+24>:	bne     cr7,0x180090c <g()+44> (Note: the branch =
target requires cfa=3D16(r1) )
   0x018008fc <+28>:	lwz     r0,20(r1)
   0x01800900 <+32>:	addi    r1,r1,16
	(Note: the cfa changed but nothing reports it here: still =
treated as cfa=3D16(r1) )

        (Later: Compare to what a more modern g++ compilers produce =
after adjusting r1.)
   0x01800904 <+36>:	mtlr    r0
   0x01800908 <+40>:	b       0x18008b8 <f()>

	(Compare here to what more modern g++ compilers produce.)
   0x0180090c <+44>:	li      r3,4
   0x01800910 <+48>:	bl      0x1810e0c <__cxa_allocate_exception@plt>
   0x01800914 <+52>:	lis     r9,385
   0x01800918 <+56>:	addi    r9,r9,3712
   0x0180091c <+60>:	lis     r4,385
   0x01800920 <+64>:	lis     r5,385
   0x01800924 <+68>:	stw     r9,0(r3)
   0x01800928 <+72>:	addi    r4,r4,3724
   0x0180092c <+76>:	addi    r5,r5,3652
   0x01800930 <+80>:	bl      0x1810e14 <__cxa_throw@plt>
End of assembler dump.

No use of DW_CFA_remember_state or DW_CFA_restore_state, despite
the tail call optimization. This is why gcc 4.2.1 tests do not
show the DW_CFA_{remember,restore}_state problem here.


g++8 toolchain:
(but a.out using /lib/libgcc_s.so.1 in order to test that library)

(gdb) disass g
Dump of assembler code for function g():
(mixed with related dwarfdump lines)
(Note: f()'s code was inlined.)

        0x01800978: <off cfa=3D00(r1) >=20
   0x01800978 <+0>:	lis     r10,385
   0x0180097c <+4>:	stwu    r1,-32(r1)

        0x01800980: <off cfa=3D32(r1) >=20
   0x01800980 <+8>:	lwz     r9,3176(r10)
   0x01800984 <+12>:	cmpwi   cr7,r9,0
   0x01800988 <+16>:	bne     cr7,0x18009ac <g()+52> (Note: branch =
target requires cfa=3D32(r1) )
   0x0180098c <+20>:	li      r9,97
   0x01800990 <+24>:	stb     r9,8(r1)
   0x01800994 <+28>:	lwz     r9,3176(r10)
   0x01800998 <+32>:	addi    r9,r9,1
   0x0180099c <+36>:	stw     r9,3176(r10)
   0x018009a0 <+40>:	lbz     r9,8(r1)
   0x018009a4 <+44>:	addi    r1,r1,32
	DW_CFA_remember_state is generate before the following change is =
made.
	(unwind-dw2.c and company do not record the cfa=3D32(r1) =
material but should)

        0x018009a8: <off cfa=3D00(r1) > (gcc 4.2.1 did not generate =
anything to cause this)
   0x018009a8 <+48>:	blr

	DW_CFA_restore_state is generated to cause the following change:
        0x018009ac: <off cfa=3D32(r1) >
	(unwind-dw2.c and company did not record the cfa=3D32(r1) =
material but should have)
	(unwind-dw2.c and company cause defaults here: cfa=3D0(r1), =
which is wrong)
   0x018009ac <+52>:	mflr    r0

        0x018009b0: <off cfa=3D32(r1) > <off r65=3Dr0 >=20
	(unwind-dw2.c and company caused defaults here: cfa=3D0(r1), =
which is wrong)
   0x018009b0 <+56>:	li      r3,4
   0x018009b4 <+60>:	stw     r0,36(r1)

        0x018009b8: <off cfa=3D32(r1) > <off r65=3D04(cfa) >=20
	(unwind-dw2.c and company caused defaults here: cfa=3D0(r1), =
which is wrong)
   0x018009b8 <+64>:	bl      0x1810cc4 <__cxa_allocate_exception@plt>
   0x018009bc <+68>:	lis     r9,385
   0x018009c0 <+72>:	lis     r5,385
   0x018009c4 <+76>:	addi    r9,r9,2916
   0x018009c8 <+80>:	lis     r4,385
   0x018009cc <+84>:	stw     r9,0(r3)
   0x018009d0 <+88>:	addi    r5,r5,3332
   0x018009d4 <+92>:	addi    r4,r4,3380
   0x018009d8 <+96>:	bl      0x1810cec <__cxa_throw@plt>
End of assembler dump.

g++8 and the like track more of the cfa changes in the exception
handling information and so use DW_CFA_{remember,restore}_state
in more types of contexts. This leads to running into the
incomplete implementation in unwind-dw2.c and company more
as well.

Based on the incorrect cfa=3D0(r1) _Unwind_RaiseException ends up
looping, looking at the same frame each time, making no progress.


powerpc64 notes:

It turns out that devel/powerpc64-gcc used for buildworld generates
code in /lib/libgcc_s.so's exception handling code that hits the
problem, blocking all c++ exception handling via that library
because of _Unwind_RaiseException never finishing.

For testing I touched the library code to avoid ending up with the
DW_CFA_{remember,restore}_state usage where it was a problem. This
allowed for simple programs to be used for illustration of the
problem in that context --and programs that do not show the
problem as well (via lack of DW_CFA_{remember,restore}_state use).

clang++ does generate DW_CFA_{remember,restore}_state in some
contexts and these have the problem too.

Any DW_CFA_restore_state where the result should not have
cfa=3D0(r1) is broken for powerpc families.



For reference:

For the g++8 based a.out:

# ldd a.out
a.out:
	libstdc++.so.6 =3D> /usr/lib/libstdc++.so.6 (0x41860000)
	libm.so.5 =3D> /lib/libm.so.5 (0x41972000)
	libgcc_s.so.1 =3D> /lib/libgcc_s.so.1 (0x419ab000)
	libc.so.7 =3D> /lib/libc.so.7 (0x419cb000)

(g++8's own libgcc_s.so does not have such problems.)

# more exception_test1.cpp=20
#include <exception>

// -O2 context used.

volatile unsigned int v =3D 1;

extern int f()
{
    volatile unsigned char c =3D 'a';
    v++; // despite volatile the access to v in g
         // was otherwise optimized out and the
         // std::exception was not followed by
         // code for f().
    return c;
}

extern void g()
{
    if (v) throw std::exception();
    f(); // Modern g++'s: ends up inlined but the problem is =
demonstrated.
}

int main(void)
{
    try {g();} // Used a separate function to avoid any potential
               // special handling of code in main.
    catch (std::exception& e) {}
    return 0;
}


=3D=3D=3D
Mark Millard
marklmi at yahoo.com
( dsl-only.net went
away in early 2018-Mar)




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?B2566AE9-C9A7-449A-83C2-5FB942EC3E52>