mailing list of musl libc
 help / color / mirror / code / Atom feed
* armv7-m segv when throwing c++ exception
@ 2019-10-02  2:00 Patrick Oppenlander
  2019-10-02  9:22 ` Szabolcs Nagy
  0 siblings, 1 reply; 8+ messages in thread
From: Patrick Oppenlander @ 2019-10-02  2:00 UTC (permalink / raw)
  To: musl

Hi all,

I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
using musl-cross-make for armv7-m.

% cat test.cpp
int main()
{
        throw 0;
}
% armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
% qemu-arm ./a.out
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
[2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out

We should be seeing something like "terminate called after throwing an
instance of 'int'".

I have an ODROID-C2 here which is a convenient place to run gdb:

$ gdb -q ./a.out
Reading symbols from ./a.out...
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
    at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
file or directory.
(gdb) bt
#0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
    at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
#1  0xf77cf902 in main () at test.cpp:3
(gdb) disas
Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
std::type_info*, void (*)(void*)):
   0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
   0xf77cf9e2 <+2>: mov r5, r0
   0xf77cf9e4 <+4>: mov r7, r1
   0xf77cf9e6 <+6>: mov r6, r2
   0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
   0xf77cf9ec <+12>: mov r4, r0
   0xf77cf9ee <+14>: mov r1, r7
   0xf77cf9f0 <+16>: mov r2, r6
   0xf77cf9f2 <+18>: mov r0, r5
=> 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
   0xf77cf9f6 <+22>: adds r3, #1
   0xf77cf9f8 <+24>: str r3, [r4, #4]
   0xf77cf9fa <+26>: bl 0xf77cf984
<__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
(*)(void*))>
   0xf77cf9fe <+30>: movs r3, #1
   0xf77cfa00 <+32>: mov r4, r0
   0xf77cfa02 <+34>: str.w r3, [r4], #40
   0xf77cfa06 <+38>: mov r0, r4
   0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
   0xf77cfa0c <+44>: mov r0, r4
   0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
   0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
End of assembler dump.
(gdb) p/x $r4
$1 = 0x1

That doesn't look good.

(gdb) disas __cxa_get_globals
Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
   0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
<__cxxabiv1::__cxa_get_globals()+16>)
   0xf77d01c6 <+2>: push {r3, lr}
   0xf77d01c8 <+4>: add r0, pc
   0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
   0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
<__cxxabiv1::__cxa_get_globals()+20>)
   0xf77d01d0 <+12>: add r0, r3
   0xf77d01d2 <+14>: pop {r3, pc}
   0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
   0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
End of assembler dump.

OK, so what did __tls_get_addr return?

(gdb) b *(__cxa_get_globals + 10)
Breakpoint 3 at 0xf77d01ce: file
../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
(gdb) run
Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
file or directory.
(gdb) p/x $r0
$3 = 0x1

That's doesn't look good either. Let's look at __tls_get_addr.

void *__tls_get_addr(tls_mod_off_t *v)
{
        pthread_t self = __pthread_self();
        return (void *)(self->dtv[v[0]] + v[1]);
}

(gdb) disas __tls_get_addr (annotations added by me)
Dump of assembler code for function __tls_get_addr:
r2 = v[0]
   0x0000c940 <+0>: ldr r2, [r0, #0]
r3 = tls
   0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
self = r3 - 120
r3 = *(r3 - 116) = self->dtv
   0x0000c946 <+6>: ldr.w r3, [r3, #-116]
r0 = v[1]
   0x0000c94a <+10>: ldr r0, [r0, #4]
r2 = *(r3 + r2 * 4) = dtv[r2]
   0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
r0 = r0 + r2
   0x0000c950 <+16>: add r0, r2
   0x0000c952 <+18>: bx lr
End of assembler dump.
(gdb) b __tls_get_addr

So after pulling out the values I end up with:
v = 0xf77f000c
0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
v[0] = 0
v[1] = 0
self = <builtin_tls> 0xf77f064c
0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
self->dtv = <builtin_tls+144> 0xf77f06dc
0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
return = dtv[v[0]] + v[1] = 1 + 0 = 1

At this point I'm out of my depth.

Where should I look next?

Thanks,

Patrick


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02  2:00 armv7-m segv when throwing c++ exception Patrick Oppenlander
@ 2019-10-02  9:22 ` Szabolcs Nagy
  2019-10-02 11:40   ` Rich Felker
  0 siblings, 1 reply; 8+ messages in thread
From: Szabolcs Nagy @ 2019-10-02  9:22 UTC (permalink / raw)
  To: musl

* Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> Hi all,
> 
> I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> using musl-cross-make for armv7-m.
> 
> % cat test.cpp
> int main()
> {
>         throw 0;
> }
> % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> % qemu-arm ./a.out
> qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> 
> We should be seeing something like "terminate called after throwing an
> instance of 'int'".

there is at least one known issue in this area:
libgcc uses pthread apis via weak refs so with static
linking they don't get linked in, however i think that
should only affect unwinding in multi-thread processes,
so this is something else

> I have an ODROID-C2 here which is a convenient place to run gdb:
> 
> $ gdb -q ./a.out
> Reading symbols from ./a.out...
> (gdb) run
> Program received signal SIGSEGV, Segmentation fault.
> 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
>     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> file or directory.
> (gdb) bt
> #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
>     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> #1  0xf77cf902 in main () at test.cpp:3
> (gdb) disas
> Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> std::type_info*, void (*)(void*)):
>    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
>    0xf77cf9e2 <+2>: mov r5, r0
>    0xf77cf9e4 <+4>: mov r7, r1
>    0xf77cf9e6 <+6>: mov r6, r2
>    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
>    0xf77cf9ec <+12>: mov r4, r0
>    0xf77cf9ee <+14>: mov r1, r7
>    0xf77cf9f0 <+16>: mov r2, r6
>    0xf77cf9f2 <+18>: mov r0, r5
> => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
>    0xf77cf9f6 <+22>: adds r3, #1
>    0xf77cf9f8 <+24>: str r3, [r4, #4]
>    0xf77cf9fa <+26>: bl 0xf77cf984
> <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> (*)(void*))>
>    0xf77cf9fe <+30>: movs r3, #1
>    0xf77cfa00 <+32>: mov r4, r0
>    0xf77cfa02 <+34>: str.w r3, [r4], #40
>    0xf77cfa06 <+38>: mov r0, r4
>    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
>    0xf77cfa0c <+44>: mov r0, r4
>    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
>    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> End of assembler dump.
> (gdb) p/x $r4
> $1 = 0x1
> 
> That doesn't look good.
> 
> (gdb) disas __cxa_get_globals
> Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
>    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> <__cxxabiv1::__cxa_get_globals()+16>)
>    0xf77d01c6 <+2>: push {r3, lr}
>    0xf77d01c8 <+4>: add r0, pc
>    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
>    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> <__cxxabiv1::__cxa_get_globals()+20>)
>    0xf77d01d0 <+12>: add r0, r3
>    0xf77d01d2 <+14>: pop {r3, pc}
>    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
>    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> End of assembler dump.

interesting, i thought the linker would relax __tls_get_addr
calls in static linked executables to local-exec access model,
but maybe that's not implemented on arm.

can you look at the readelf -aW a.out and see if the linker
left any dynamic tls relocs in the exe?

> 
> OK, so what did __tls_get_addr return?
> 
> (gdb) b *(__cxa_get_globals + 10)
> Breakpoint 3 at 0xf77d01ce: file
> ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> (gdb) run
> Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> file or directory.
> (gdb) p/x $r0
> $3 = 0x1
> 
> That's doesn't look good either. Let's look at __tls_get_addr.
> 
> void *__tls_get_addr(tls_mod_off_t *v)
> {
>         pthread_t self = __pthread_self();
>         return (void *)(self->dtv[v[0]] + v[1]);
> }
> 
> (gdb) disas __tls_get_addr (annotations added by me)
> Dump of assembler code for function __tls_get_addr:
> r2 = v[0]
>    0x0000c940 <+0>: ldr r2, [r0, #0]
> r3 = tls
>    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> self = r3 - 120
> r3 = *(r3 - 116) = self->dtv
>    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> r0 = v[1]
>    0x0000c94a <+10>: ldr r0, [r0, #4]
> r2 = *(r3 + r2 * 4) = dtv[r2]
>    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> r0 = r0 + r2
>    0x0000c950 <+16>: add r0, r2
>    0x0000c952 <+18>: bx lr
> End of assembler dump.
> (gdb) b __tls_get_addr
> 
> So after pulling out the values I end up with:
> v = 0xf77f000c
> 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> v[0] = 0
> v[1] = 0
> self = <builtin_tls> 0xf77f064c
> 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> self->dtv = <builtin_tls+144> 0xf77f06dc
> 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> return = dtv[v[0]] + v[1] = 1 + 0 = 1
> 
> At this point I'm out of my depth.

dtv[0] is special and just stores the length of the dtv
(which is correctly 1, meaning there is 1 elf module with tls).

so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
the tls address of the executable) i think v[0] should be set
up by the static linker so it may be a binutils bug.
(or maybe static linking is special and then dtv[0] should be
set equal to dtv[1]?)

> 
> Where should I look next?
> 
> Thanks,
> 
> Patrick


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02  9:22 ` Szabolcs Nagy
@ 2019-10-02 11:40   ` Rich Felker
  2019-10-02 11:49     ` Rich Felker
  0 siblings, 1 reply; 8+ messages in thread
From: Rich Felker @ 2019-10-02 11:40 UTC (permalink / raw)
  To: musl

On Wed, Oct 02, 2019 at 11:22:33AM +0200, Szabolcs Nagy wrote:
> * Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> > Hi all,
> > 
> > I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> > using musl-cross-make for armv7-m.
> > 
> > % cat test.cpp
> > int main()
> > {
> >         throw 0;
> > }
> > % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> > % qemu-arm ./a.out
> > qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> > [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> > 
> > We should be seeing something like "terminate called after throwing an
> > instance of 'int'".
> 
> there is at least one known issue in this area:
> libgcc uses pthread apis via weak refs so with static
> linking they don't get linked in, however i think that
> should only affect unwinding in multi-thread processes,
> so this is something else
> 
> > I have an ODROID-C2 here which is a convenient place to run gdb:
> > 
> > $ gdb -q ./a.out
> > Reading symbols from ./a.out...
> > (gdb) run
> > Program received signal SIGSEGV, Segmentation fault.
> > 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> > file or directory.
> > (gdb) bt
> > #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > #1  0xf77cf902 in main () at test.cpp:3
> > (gdb) disas
> > Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> > std::type_info*, void (*)(void*)):
> >    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
> >    0xf77cf9e2 <+2>: mov r5, r0
> >    0xf77cf9e4 <+4>: mov r7, r1
> >    0xf77cf9e6 <+6>: mov r6, r2
> >    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
> >    0xf77cf9ec <+12>: mov r4, r0
> >    0xf77cf9ee <+14>: mov r1, r7
> >    0xf77cf9f0 <+16>: mov r2, r6
> >    0xf77cf9f2 <+18>: mov r0, r5
> > => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
> >    0xf77cf9f6 <+22>: adds r3, #1
> >    0xf77cf9f8 <+24>: str r3, [r4, #4]
> >    0xf77cf9fa <+26>: bl 0xf77cf984
> > <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> > (*)(void*))>
> >    0xf77cf9fe <+30>: movs r3, #1
> >    0xf77cfa00 <+32>: mov r4, r0
> >    0xf77cfa02 <+34>: str.w r3, [r4], #40
> >    0xf77cfa06 <+38>: mov r0, r4
> >    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
> >    0xf77cfa0c <+44>: mov r0, r4
> >    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
> >    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> > End of assembler dump.
> > (gdb) p/x $r4
> > $1 = 0x1
> > 
> > That doesn't look good.
> > 
> > (gdb) disas __cxa_get_globals
> > Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
> >    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> > <__cxxabiv1::__cxa_get_globals()+16>)
> >    0xf77d01c6 <+2>: push {r3, lr}
> >    0xf77d01c8 <+4>: add r0, pc
> >    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
> >    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> > <__cxxabiv1::__cxa_get_globals()+20>)
> >    0xf77d01d0 <+12>: add r0, r3
> >    0xf77d01d2 <+14>: pop {r3, pc}
> >    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
> >    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> > End of assembler dump.
> 
> interesting, i thought the linker would relax __tls_get_addr
> calls in static linked executables to local-exec access model,
> but maybe that's not implemented on arm.
> 
> can you look at the readelf -aW a.out and see if the linker
> left any dynamic tls relocs in the exe?
> 
> > 
> > OK, so what did __tls_get_addr return?
> > 
> > (gdb) b *(__cxa_get_globals + 10)
> > Breakpoint 3 at 0xf77d01ce: file
> > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> > (gdb) run
> > Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> > 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> > file or directory.
> > (gdb) p/x $r0
> > $3 = 0x1
> > 
> > That's doesn't look good either. Let's look at __tls_get_addr.
> > 
> > void *__tls_get_addr(tls_mod_off_t *v)
> > {
> >         pthread_t self = __pthread_self();
> >         return (void *)(self->dtv[v[0]] + v[1]);
> > }
> > 
> > (gdb) disas __tls_get_addr (annotations added by me)
> > Dump of assembler code for function __tls_get_addr:
> > r2 = v[0]
> >    0x0000c940 <+0>: ldr r2, [r0, #0]
> > r3 = tls
> >    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> > self = r3 - 120
> > r3 = *(r3 - 116) = self->dtv
> >    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> > r0 = v[1]
> >    0x0000c94a <+10>: ldr r0, [r0, #4]
> > r2 = *(r3 + r2 * 4) = dtv[r2]
> >    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> > r0 = r0 + r2
> >    0x0000c950 <+16>: add r0, r2
> >    0x0000c952 <+18>: bx lr
> > End of assembler dump.
> > (gdb) b __tls_get_addr
> > 
> > So after pulling out the values I end up with:
> > v = 0xf77f000c
> > 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> > v[0] = 0
> > v[1] = 0
> > self = <builtin_tls> 0xf77f064c
> > 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> > self->dtv = <builtin_tls+144> 0xf77f06dc
> > 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> > return = dtv[v[0]] + v[1] = 1 + 0 = 1
> > 
> > At this point I'm out of my depth.
> 
> dtv[0] is special and just stores the length of the dtv
> (which is correctly 1, meaning there is 1 elf module with tls).
> 
> so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
> the tls address of the executable) i think v[0] should be set
> up by the static linker so it may be a binutils bug.
> (or maybe static linking is special and then dtv[0] should be
> set equal to dtv[1]?)

MIPS had the exact same bug; I think it's fixed upstream now since we
had the patch for binutils 2.27 in mcm but dropped it in 2.32:

https://github.com/richfelker/musl-cross-make/blob/master/patches/binutils-2.27/0004-mips-pie-tls.diff

It affects static PIE only, so -no-pie should be able to work around
it (conditional for setting the 1 was essentially !ET_DYN instead of
is_executable).

We could work around this in musl in one of two ways: rcrt1 could
process DTPMOD relocs and write a 1, or the static version of
__init_tls could just duplicate dtv[1] in dtv[0] (the latter is only
valid up until we add static-linked dlopen, if we ever do that).
However I was fairly strongly against doing that for MIPS, since it
was a binutils bug and only affected static linking (so it's not a
runtime ABI issue; the binary either works at link-time or it doesn't)
and I'd lean towards treating ARM the same.

It should be easy to make and apply the binutils patch to fix this,
and shouldn't be too hard to grep for the same bug in all archs (since
binutils nicely duplicates this logic for every single arch, for no
reason whatsoever).

Rich


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02 11:40   ` Rich Felker
@ 2019-10-02 11:49     ` Rich Felker
  2019-10-02 12:25       ` Rich Felker
  0 siblings, 1 reply; 8+ messages in thread
From: Rich Felker @ 2019-10-02 11:49 UTC (permalink / raw)
  To: musl

[-- Attachment #1: Type: text/plain, Size: 7645 bytes --]

On Wed, Oct 02, 2019 at 07:40:45AM -0400, Rich Felker wrote:
> On Wed, Oct 02, 2019 at 11:22:33AM +0200, Szabolcs Nagy wrote:
> > * Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> > > Hi all,
> > > 
> > > I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> > > using musl-cross-make for armv7-m.
> > > 
> > > % cat test.cpp
> > > int main()
> > > {
> > >         throw 0;
> > > }
> > > % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> > > % qemu-arm ./a.out
> > > qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> > > [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> > > 
> > > We should be seeing something like "terminate called after throwing an
> > > instance of 'int'".
> > 
> > there is at least one known issue in this area:
> > libgcc uses pthread apis via weak refs so with static
> > linking they don't get linked in, however i think that
> > should only affect unwinding in multi-thread processes,
> > so this is something else
> > 
> > > I have an ODROID-C2 here which is a convenient place to run gdb:
> > > 
> > > $ gdb -q ./a.out
> > > Reading symbols from ./a.out...
> > > (gdb) run
> > > Program received signal SIGSEGV, Segmentation fault.
> > > 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> > > file or directory.
> > > (gdb) bt
> > > #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > #1  0xf77cf902 in main () at test.cpp:3
> > > (gdb) disas
> > > Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> > > std::type_info*, void (*)(void*)):
> > >    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
> > >    0xf77cf9e2 <+2>: mov r5, r0
> > >    0xf77cf9e4 <+4>: mov r7, r1
> > >    0xf77cf9e6 <+6>: mov r6, r2
> > >    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
> > >    0xf77cf9ec <+12>: mov r4, r0
> > >    0xf77cf9ee <+14>: mov r1, r7
> > >    0xf77cf9f0 <+16>: mov r2, r6
> > >    0xf77cf9f2 <+18>: mov r0, r5
> > > => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
> > >    0xf77cf9f6 <+22>: adds r3, #1
> > >    0xf77cf9f8 <+24>: str r3, [r4, #4]
> > >    0xf77cf9fa <+26>: bl 0xf77cf984
> > > <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> > > (*)(void*))>
> > >    0xf77cf9fe <+30>: movs r3, #1
> > >    0xf77cfa00 <+32>: mov r4, r0
> > >    0xf77cfa02 <+34>: str.w r3, [r4], #40
> > >    0xf77cfa06 <+38>: mov r0, r4
> > >    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
> > >    0xf77cfa0c <+44>: mov r0, r4
> > >    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
> > >    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> > > End of assembler dump.
> > > (gdb) p/x $r4
> > > $1 = 0x1
> > > 
> > > That doesn't look good.
> > > 
> > > (gdb) disas __cxa_get_globals
> > > Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
> > >    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> > > <__cxxabiv1::__cxa_get_globals()+16>)
> > >    0xf77d01c6 <+2>: push {r3, lr}
> > >    0xf77d01c8 <+4>: add r0, pc
> > >    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
> > >    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> > > <__cxxabiv1::__cxa_get_globals()+20>)
> > >    0xf77d01d0 <+12>: add r0, r3
> > >    0xf77d01d2 <+14>: pop {r3, pc}
> > >    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
> > >    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> > > End of assembler dump.
> > 
> > interesting, i thought the linker would relax __tls_get_addr
> > calls in static linked executables to local-exec access model,
> > but maybe that's not implemented on arm.
> > 
> > can you look at the readelf -aW a.out and see if the linker
> > left any dynamic tls relocs in the exe?
> > 
> > > 
> > > OK, so what did __tls_get_addr return?
> > > 
> > > (gdb) b *(__cxa_get_globals + 10)
> > > Breakpoint 3 at 0xf77d01ce: file
> > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> > > (gdb) run
> > > Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> > > 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> > > file or directory.
> > > (gdb) p/x $r0
> > > $3 = 0x1
> > > 
> > > That's doesn't look good either. Let's look at __tls_get_addr.
> > > 
> > > void *__tls_get_addr(tls_mod_off_t *v)
> > > {
> > >         pthread_t self = __pthread_self();
> > >         return (void *)(self->dtv[v[0]] + v[1]);
> > > }
> > > 
> > > (gdb) disas __tls_get_addr (annotations added by me)
> > > Dump of assembler code for function __tls_get_addr:
> > > r2 = v[0]
> > >    0x0000c940 <+0>: ldr r2, [r0, #0]
> > > r3 = tls
> > >    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> > > self = r3 - 120
> > > r3 = *(r3 - 116) = self->dtv
> > >    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> > > r0 = v[1]
> > >    0x0000c94a <+10>: ldr r0, [r0, #4]
> > > r2 = *(r3 + r2 * 4) = dtv[r2]
> > >    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> > > r0 = r0 + r2
> > >    0x0000c950 <+16>: add r0, r2
> > >    0x0000c952 <+18>: bx lr
> > > End of assembler dump.
> > > (gdb) b __tls_get_addr
> > > 
> > > So after pulling out the values I end up with:
> > > v = 0xf77f000c
> > > 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> > > v[0] = 0
> > > v[1] = 0
> > > self = <builtin_tls> 0xf77f064c
> > > 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> > > self->dtv = <builtin_tls+144> 0xf77f06dc
> > > 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> > > return = dtv[v[0]] + v[1] = 1 + 0 = 1
> > > 
> > > At this point I'm out of my depth.
> > 
> > dtv[0] is special and just stores the length of the dtv
> > (which is correctly 1, meaning there is 1 elf module with tls).
> > 
> > so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
> > the tls address of the executable) i think v[0] should be set
> > up by the static linker so it may be a binutils bug.
> > (or maybe static linking is special and then dtv[0] should be
> > set equal to dtv[1]?)
> 
> MIPS had the exact same bug; I think it's fixed upstream now since we
> had the patch for binutils 2.27 in mcm but dropped it in 2.32:
> 
> https://github.com/richfelker/musl-cross-make/blob/master/patches/binutils-2.27/0004-mips-pie-tls.diff
> 
> It affects static PIE only, so -no-pie should be able to work around
> it (conditional for setting the 1 was essentially !ET_DYN instead of
> is_executable).
> 
> We could work around this in musl in one of two ways: rcrt1 could
> process DTPMOD relocs and write a 1, or the static version of
> __init_tls could just duplicate dtv[1] in dtv[0] (the latter is only
> valid up until we add static-linked dlopen, if we ever do that).
> However I was fairly strongly against doing that for MIPS, since it
> was a binutils bug and only affected static linking (so it's not a
> runtime ABI issue; the binary either works at link-time or it doesn't)
> and I'd lean towards treating ARM the same.
> 
> It should be easy to make and apply the binutils patch to fix this,
> and shouldn't be too hard to grep for the same bug in all archs (since
> binutils nicely duplicates this logic for every single arch, for no
> reason whatsoever).

Attached patch is untested but should fix it.

Rich

[-- Attachment #2: 0002-arm-pie-tls.diff --]
[-- Type: text/plain, Size: 364 bytes --]

--- binutils-2.32/bfd/elf32-arm.c.orig	2019-10-02 07:47:36.153918869 -0400
+++ binutils-2.32/bfd/elf32-arm.c	2019-10-02 07:47:45.405988597 -0400
@@ -11624,7 +11624,7 @@
 	  {
 	    /* If we don't know the module number, create a relocation
 	       for it.  */
-	    if (bfd_link_pic (info))
+	    if (bfd_link_dll (info))
 	      {
 		Elf_Internal_Rela outrel;
 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02 11:49     ` Rich Felker
@ 2019-10-02 12:25       ` Rich Felker
  2019-10-02 12:49         ` Rich Felker
  0 siblings, 1 reply; 8+ messages in thread
From: Rich Felker @ 2019-10-02 12:25 UTC (permalink / raw)
  To: musl

On Wed, Oct 02, 2019 at 07:49:55AM -0400, Rich Felker wrote:
> On Wed, Oct 02, 2019 at 07:40:45AM -0400, Rich Felker wrote:
> > On Wed, Oct 02, 2019 at 11:22:33AM +0200, Szabolcs Nagy wrote:
> > > * Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> > > > Hi all,
> > > > 
> > > > I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> > > > using musl-cross-make for armv7-m.
> > > > 
> > > > % cat test.cpp
> > > > int main()
> > > > {
> > > >         throw 0;
> > > > }
> > > > % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> > > > % qemu-arm ./a.out
> > > > qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> > > > [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> > > > 
> > > > We should be seeing something like "terminate called after throwing an
> > > > instance of 'int'".
> > > 
> > > there is at least one known issue in this area:
> > > libgcc uses pthread apis via weak refs so with static
> > > linking they don't get linked in, however i think that
> > > should only affect unwinding in multi-thread processes,
> > > so this is something else
> > > 
> > > > I have an ODROID-C2 here which is a convenient place to run gdb:
> > > > 
> > > > $ gdb -q ./a.out
> > > > Reading symbols from ./a.out...
> > > > (gdb) run
> > > > Program received signal SIGSEGV, Segmentation fault.
> > > > 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> > > > file or directory.
> > > > (gdb) bt
> > > > #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > #1  0xf77cf902 in main () at test.cpp:3
> > > > (gdb) disas
> > > > Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> > > > std::type_info*, void (*)(void*)):
> > > >    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
> > > >    0xf77cf9e2 <+2>: mov r5, r0
> > > >    0xf77cf9e4 <+4>: mov r7, r1
> > > >    0xf77cf9e6 <+6>: mov r6, r2
> > > >    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
> > > >    0xf77cf9ec <+12>: mov r4, r0
> > > >    0xf77cf9ee <+14>: mov r1, r7
> > > >    0xf77cf9f0 <+16>: mov r2, r6
> > > >    0xf77cf9f2 <+18>: mov r0, r5
> > > > => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
> > > >    0xf77cf9f6 <+22>: adds r3, #1
> > > >    0xf77cf9f8 <+24>: str r3, [r4, #4]
> > > >    0xf77cf9fa <+26>: bl 0xf77cf984
> > > > <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> > > > (*)(void*))>
> > > >    0xf77cf9fe <+30>: movs r3, #1
> > > >    0xf77cfa00 <+32>: mov r4, r0
> > > >    0xf77cfa02 <+34>: str.w r3, [r4], #40
> > > >    0xf77cfa06 <+38>: mov r0, r4
> > > >    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
> > > >    0xf77cfa0c <+44>: mov r0, r4
> > > >    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
> > > >    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> > > > End of assembler dump.
> > > > (gdb) p/x $r4
> > > > $1 = 0x1
> > > > 
> > > > That doesn't look good.
> > > > 
> > > > (gdb) disas __cxa_get_globals
> > > > Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
> > > >    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> > > > <__cxxabiv1::__cxa_get_globals()+16>)
> > > >    0xf77d01c6 <+2>: push {r3, lr}
> > > >    0xf77d01c8 <+4>: add r0, pc
> > > >    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
> > > >    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> > > > <__cxxabiv1::__cxa_get_globals()+20>)
> > > >    0xf77d01d0 <+12>: add r0, r3
> > > >    0xf77d01d2 <+14>: pop {r3, pc}
> > > >    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
> > > >    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> > > > End of assembler dump.
> > > 
> > > interesting, i thought the linker would relax __tls_get_addr
> > > calls in static linked executables to local-exec access model,
> > > but maybe that's not implemented on arm.
> > > 
> > > can you look at the readelf -aW a.out and see if the linker
> > > left any dynamic tls relocs in the exe?
> > > 
> > > > 
> > > > OK, so what did __tls_get_addr return?
> > > > 
> > > > (gdb) b *(__cxa_get_globals + 10)
> > > > Breakpoint 3 at 0xf77d01ce: file
> > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> > > > (gdb) run
> > > > Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> > > > 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> > > > file or directory.
> > > > (gdb) p/x $r0
> > > > $3 = 0x1
> > > > 
> > > > That's doesn't look good either. Let's look at __tls_get_addr.
> > > > 
> > > > void *__tls_get_addr(tls_mod_off_t *v)
> > > > {
> > > >         pthread_t self = __pthread_self();
> > > >         return (void *)(self->dtv[v[0]] + v[1]);
> > > > }
> > > > 
> > > > (gdb) disas __tls_get_addr (annotations added by me)
> > > > Dump of assembler code for function __tls_get_addr:
> > > > r2 = v[0]
> > > >    0x0000c940 <+0>: ldr r2, [r0, #0]
> > > > r3 = tls
> > > >    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> > > > self = r3 - 120
> > > > r3 = *(r3 - 116) = self->dtv
> > > >    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> > > > r0 = v[1]
> > > >    0x0000c94a <+10>: ldr r0, [r0, #4]
> > > > r2 = *(r3 + r2 * 4) = dtv[r2]
> > > >    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> > > > r0 = r0 + r2
> > > >    0x0000c950 <+16>: add r0, r2
> > > >    0x0000c952 <+18>: bx lr
> > > > End of assembler dump.
> > > > (gdb) b __tls_get_addr
> > > > 
> > > > So after pulling out the values I end up with:
> > > > v = 0xf77f000c
> > > > 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> > > > v[0] = 0
> > > > v[1] = 0
> > > > self = <builtin_tls> 0xf77f064c
> > > > 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> > > > self->dtv = <builtin_tls+144> 0xf77f06dc
> > > > 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> > > > return = dtv[v[0]] + v[1] = 1 + 0 = 1
> > > > 
> > > > At this point I'm out of my depth.
> > > 
> > > dtv[0] is special and just stores the length of the dtv
> > > (which is correctly 1, meaning there is 1 elf module with tls).
> > > 
> > > so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
> > > the tls address of the executable) i think v[0] should be set
> > > up by the static linker so it may be a binutils bug.
> > > (or maybe static linking is special and then dtv[0] should be
> > > set equal to dtv[1]?)
> > 
> > MIPS had the exact same bug; I think it's fixed upstream now since we
> > had the patch for binutils 2.27 in mcm but dropped it in 2.32:
> > 
> > https://github.com/richfelker/musl-cross-make/blob/master/patches/binutils-2.27/0004-mips-pie-tls.diff
> > 
> > It affects static PIE only, so -no-pie should be able to work around
> > it (conditional for setting the 1 was essentially !ET_DYN instead of
> > is_executable).
> > 
> > We could work around this in musl in one of two ways: rcrt1 could
> > process DTPMOD relocs and write a 1, or the static version of
> > __init_tls could just duplicate dtv[1] in dtv[0] (the latter is only
> > valid up until we add static-linked dlopen, if we ever do that).
> > However I was fairly strongly against doing that for MIPS, since it
> > was a binutils bug and only affected static linking (so it's not a
> > runtime ABI issue; the binary either works at link-time or it doesn't)
> > and I'd lean towards treating ARM the same.
> > 
> > It should be easy to make and apply the binutils patch to fix this,
> > and shouldn't be too hard to grep for the same bug in all archs (since
> > binutils nicely duplicates this logic for every single arch, for no
> > reason whatsoever).
> 
> Attached patch is untested but should fix it.

It's missing a second instance and doesn't work. I'll send an updated
patch after testing.

Rich


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02 12:25       ` Rich Felker
@ 2019-10-02 12:49         ` Rich Felker
  2019-10-02 14:55           ` Rich Felker
  0 siblings, 1 reply; 8+ messages in thread
From: Rich Felker @ 2019-10-02 12:49 UTC (permalink / raw)
  To: musl

[-- Attachment #1: Type: text/plain, Size: 8627 bytes --]

On Wed, Oct 02, 2019 at 08:25:43AM -0400, Rich Felker wrote:
> On Wed, Oct 02, 2019 at 07:49:55AM -0400, Rich Felker wrote:
> > On Wed, Oct 02, 2019 at 07:40:45AM -0400, Rich Felker wrote:
> > > On Wed, Oct 02, 2019 at 11:22:33AM +0200, Szabolcs Nagy wrote:
> > > > * Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> > > > > Hi all,
> > > > > 
> > > > > I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> > > > > using musl-cross-make for armv7-m.
> > > > > 
> > > > > % cat test.cpp
> > > > > int main()
> > > > > {
> > > > >         throw 0;
> > > > > }
> > > > > % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> > > > > % qemu-arm ./a.out
> > > > > qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> > > > > [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> > > > > 
> > > > > We should be seeing something like "terminate called after throwing an
> > > > > instance of 'int'".
> > > > 
> > > > there is at least one known issue in this area:
> > > > libgcc uses pthread apis via weak refs so with static
> > > > linking they don't get linked in, however i think that
> > > > should only affect unwinding in multi-thread processes,
> > > > so this is something else
> > > > 
> > > > > I have an ODROID-C2 here which is a convenient place to run gdb:
> > > > > 
> > > > > $ gdb -q ./a.out
> > > > > Reading symbols from ./a.out...
> > > > > (gdb) run
> > > > > Program received signal SIGSEGV, Segmentation fault.
> > > > > 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > > 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> > > > > file or directory.
> > > > > (gdb) bt
> > > > > #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > > #1  0xf77cf902 in main () at test.cpp:3
> > > > > (gdb) disas
> > > > > Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> > > > > std::type_info*, void (*)(void*)):
> > > > >    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
> > > > >    0xf77cf9e2 <+2>: mov r5, r0
> > > > >    0xf77cf9e4 <+4>: mov r7, r1
> > > > >    0xf77cf9e6 <+6>: mov r6, r2
> > > > >    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
> > > > >    0xf77cf9ec <+12>: mov r4, r0
> > > > >    0xf77cf9ee <+14>: mov r1, r7
> > > > >    0xf77cf9f0 <+16>: mov r2, r6
> > > > >    0xf77cf9f2 <+18>: mov r0, r5
> > > > > => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
> > > > >    0xf77cf9f6 <+22>: adds r3, #1
> > > > >    0xf77cf9f8 <+24>: str r3, [r4, #4]
> > > > >    0xf77cf9fa <+26>: bl 0xf77cf984
> > > > > <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> > > > > (*)(void*))>
> > > > >    0xf77cf9fe <+30>: movs r3, #1
> > > > >    0xf77cfa00 <+32>: mov r4, r0
> > > > >    0xf77cfa02 <+34>: str.w r3, [r4], #40
> > > > >    0xf77cfa06 <+38>: mov r0, r4
> > > > >    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
> > > > >    0xf77cfa0c <+44>: mov r0, r4
> > > > >    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
> > > > >    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> > > > > End of assembler dump.
> > > > > (gdb) p/x $r4
> > > > > $1 = 0x1
> > > > > 
> > > > > That doesn't look good.
> > > > > 
> > > > > (gdb) disas __cxa_get_globals
> > > > > Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
> > > > >    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> > > > > <__cxxabiv1::__cxa_get_globals()+16>)
> > > > >    0xf77d01c6 <+2>: push {r3, lr}
> > > > >    0xf77d01c8 <+4>: add r0, pc
> > > > >    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
> > > > >    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> > > > > <__cxxabiv1::__cxa_get_globals()+20>)
> > > > >    0xf77d01d0 <+12>: add r0, r3
> > > > >    0xf77d01d2 <+14>: pop {r3, pc}
> > > > >    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
> > > > >    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> > > > > End of assembler dump.
> > > > 
> > > > interesting, i thought the linker would relax __tls_get_addr
> > > > calls in static linked executables to local-exec access model,
> > > > but maybe that's not implemented on arm.
> > > > 
> > > > can you look at the readelf -aW a.out and see if the linker
> > > > left any dynamic tls relocs in the exe?
> > > > 
> > > > > 
> > > > > OK, so what did __tls_get_addr return?
> > > > > 
> > > > > (gdb) b *(__cxa_get_globals + 10)
> > > > > Breakpoint 3 at 0xf77d01ce: file
> > > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> > > > > (gdb) run
> > > > > Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> > > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> > > > > 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> > > > > file or directory.
> > > > > (gdb) p/x $r0
> > > > > $3 = 0x1
> > > > > 
> > > > > That's doesn't look good either. Let's look at __tls_get_addr.
> > > > > 
> > > > > void *__tls_get_addr(tls_mod_off_t *v)
> > > > > {
> > > > >         pthread_t self = __pthread_self();
> > > > >         return (void *)(self->dtv[v[0]] + v[1]);
> > > > > }
> > > > > 
> > > > > (gdb) disas __tls_get_addr (annotations added by me)
> > > > > Dump of assembler code for function __tls_get_addr:
> > > > > r2 = v[0]
> > > > >    0x0000c940 <+0>: ldr r2, [r0, #0]
> > > > > r3 = tls
> > > > >    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> > > > > self = r3 - 120
> > > > > r3 = *(r3 - 116) = self->dtv
> > > > >    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> > > > > r0 = v[1]
> > > > >    0x0000c94a <+10>: ldr r0, [r0, #4]
> > > > > r2 = *(r3 + r2 * 4) = dtv[r2]
> > > > >    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> > > > > r0 = r0 + r2
> > > > >    0x0000c950 <+16>: add r0, r2
> > > > >    0x0000c952 <+18>: bx lr
> > > > > End of assembler dump.
> > > > > (gdb) b __tls_get_addr
> > > > > 
> > > > > So after pulling out the values I end up with:
> > > > > v = 0xf77f000c
> > > > > 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> > > > > v[0] = 0
> > > > > v[1] = 0
> > > > > self = <builtin_tls> 0xf77f064c
> > > > > 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> > > > > self->dtv = <builtin_tls+144> 0xf77f06dc
> > > > > 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> > > > > return = dtv[v[0]] + v[1] = 1 + 0 = 1
> > > > > 
> > > > > At this point I'm out of my depth.
> > > > 
> > > > dtv[0] is special and just stores the length of the dtv
> > > > (which is correctly 1, meaning there is 1 elf module with tls).
> > > > 
> > > > so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
> > > > the tls address of the executable) i think v[0] should be set
> > > > up by the static linker so it may be a binutils bug.
> > > > (or maybe static linking is special and then dtv[0] should be
> > > > set equal to dtv[1]?)
> > > 
> > > MIPS had the exact same bug; I think it's fixed upstream now since we
> > > had the patch for binutils 2.27 in mcm but dropped it in 2.32:
> > > 
> > > https://github.com/richfelker/musl-cross-make/blob/master/patches/binutils-2.27/0004-mips-pie-tls.diff
> > > 
> > > It affects static PIE only, so -no-pie should be able to work around
> > > it (conditional for setting the 1 was essentially !ET_DYN instead of
> > > is_executable).
> > > 
> > > We could work around this in musl in one of two ways: rcrt1 could
> > > process DTPMOD relocs and write a 1, or the static version of
> > > __init_tls could just duplicate dtv[1] in dtv[0] (the latter is only
> > > valid up until we add static-linked dlopen, if we ever do that).
> > > However I was fairly strongly against doing that for MIPS, since it
> > > was a binutils bug and only affected static linking (so it's not a
> > > runtime ABI issue; the binary either works at link-time or it doesn't)
> > > and I'd lean towards treating ARM the same.
> > > 
> > > It should be easy to make and apply the binutils patch to fix this,
> > > and shouldn't be too hard to grep for the same bug in all archs (since
> > > binutils nicely duplicates this logic for every single arch, for no
> > > reason whatsoever).
> > 
> > Attached patch is untested but should fix it.
> 
> It's missing a second instance and doesn't work. I'll send an updated
> patch after testing.

Attached works for me.

Rich

[-- Attachment #2: 0002-arm-pie-tls.diff --]
[-- Type: text/plain, Size: 700 bytes --]

--- binutils-2.32/bfd/elf32-arm.c.orig	2019-10-02 07:47:36.153918869 -0400
+++ binutils-2.32/bfd/elf32-arm.c	2019-10-02 08:37:09.108263016 -0400
@@ -11624,7 +11624,7 @@
 	  {
 	    /* If we don't know the module number, create a relocation
 	       for it.  */
-	    if (bfd_link_pic (info))
+	    if (bfd_link_dll (info))
 	      {
 		Elf_Internal_Rela outrel;
 
@@ -11728,7 +11728,7 @@
 	       now, and emit any relocations.  If both an IE GOT and a
 	       GD GOT are necessary, we emit the GD first.  */
 
-	    if ((bfd_link_pic (info) || indx != 0)
+	    if ((bfd_link_dll (info) || indx != 0)
 		&& (h == NULL
 		    || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
 			&& !resolved_to_zero)

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02 12:49         ` Rich Felker
@ 2019-10-02 14:55           ` Rich Felker
  2019-10-02 22:34             ` Patrick Oppenlander
  0 siblings, 1 reply; 8+ messages in thread
From: Rich Felker @ 2019-10-02 14:55 UTC (permalink / raw)
  To: musl

On Wed, Oct 02, 2019 at 08:49:12AM -0400, Rich Felker wrote:
> On Wed, Oct 02, 2019 at 08:25:43AM -0400, Rich Felker wrote:
> > On Wed, Oct 02, 2019 at 07:49:55AM -0400, Rich Felker wrote:
> > > On Wed, Oct 02, 2019 at 07:40:45AM -0400, Rich Felker wrote:
> > > > On Wed, Oct 02, 2019 at 11:22:33AM +0200, Szabolcs Nagy wrote:
> > > > > * Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> > > > > > Hi all,
> > > > > > 
> > > > > > I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> > > > > > using musl-cross-make for armv7-m.
> > > > > > 
> > > > > > % cat test.cpp
> > > > > > int main()
> > > > > > {
> > > > > >         throw 0;
> > > > > > }
> > > > > > % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> > > > > > % qemu-arm ./a.out
> > > > > > qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> > > > > > [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> > > > > > 
> > > > > > We should be seeing something like "terminate called after throwing an
> > > > > > instance of 'int'".
> > > > > 
> > > > > there is at least one known issue in this area:
> > > > > libgcc uses pthread apis via weak refs so with static
> > > > > linking they don't get linked in, however i think that
> > > > > should only affect unwinding in multi-thread processes,
> > > > > so this is something else
> > > > > 
> > > > > > I have an ODROID-C2 here which is a convenient place to run gdb:
> > > > > > 
> > > > > > $ gdb -q ./a.out
> > > > > > Reading symbols from ./a.out...
> > > > > > (gdb) run
> > > > > > Program received signal SIGSEGV, Segmentation fault.
> > > > > > 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > > > 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> > > > > > file or directory.
> > > > > > (gdb) bt
> > > > > > #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > > > #1  0xf77cf902 in main () at test.cpp:3
> > > > > > (gdb) disas
> > > > > > Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> > > > > > std::type_info*, void (*)(void*)):
> > > > > >    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
> > > > > >    0xf77cf9e2 <+2>: mov r5, r0
> > > > > >    0xf77cf9e4 <+4>: mov r7, r1
> > > > > >    0xf77cf9e6 <+6>: mov r6, r2
> > > > > >    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
> > > > > >    0xf77cf9ec <+12>: mov r4, r0
> > > > > >    0xf77cf9ee <+14>: mov r1, r7
> > > > > >    0xf77cf9f0 <+16>: mov r2, r6
> > > > > >    0xf77cf9f2 <+18>: mov r0, r5
> > > > > > => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
> > > > > >    0xf77cf9f6 <+22>: adds r3, #1
> > > > > >    0xf77cf9f8 <+24>: str r3, [r4, #4]
> > > > > >    0xf77cf9fa <+26>: bl 0xf77cf984
> > > > > > <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> > > > > > (*)(void*))>
> > > > > >    0xf77cf9fe <+30>: movs r3, #1
> > > > > >    0xf77cfa00 <+32>: mov r4, r0
> > > > > >    0xf77cfa02 <+34>: str.w r3, [r4], #40
> > > > > >    0xf77cfa06 <+38>: mov r0, r4
> > > > > >    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
> > > > > >    0xf77cfa0c <+44>: mov r0, r4
> > > > > >    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
> > > > > >    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> > > > > > End of assembler dump.
> > > > > > (gdb) p/x $r4
> > > > > > $1 = 0x1
> > > > > > 
> > > > > > That doesn't look good.
> > > > > > 
> > > > > > (gdb) disas __cxa_get_globals
> > > > > > Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
> > > > > >    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> > > > > > <__cxxabiv1::__cxa_get_globals()+16>)
> > > > > >    0xf77d01c6 <+2>: push {r3, lr}
> > > > > >    0xf77d01c8 <+4>: add r0, pc
> > > > > >    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
> > > > > >    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> > > > > > <__cxxabiv1::__cxa_get_globals()+20>)
> > > > > >    0xf77d01d0 <+12>: add r0, r3
> > > > > >    0xf77d01d2 <+14>: pop {r3, pc}
> > > > > >    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
> > > > > >    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> > > > > > End of assembler dump.
> > > > > 
> > > > > interesting, i thought the linker would relax __tls_get_addr
> > > > > calls in static linked executables to local-exec access model,
> > > > > but maybe that's not implemented on arm.
> > > > > 
> > > > > can you look at the readelf -aW a.out and see if the linker
> > > > > left any dynamic tls relocs in the exe?
> > > > > 
> > > > > > 
> > > > > > OK, so what did __tls_get_addr return?
> > > > > > 
> > > > > > (gdb) b *(__cxa_get_globals + 10)
> > > > > > Breakpoint 3 at 0xf77d01ce: file
> > > > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> > > > > > (gdb) run
> > > > > > Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> > > > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> > > > > > 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> > > > > > file or directory.
> > > > > > (gdb) p/x $r0
> > > > > > $3 = 0x1
> > > > > > 
> > > > > > That's doesn't look good either. Let's look at __tls_get_addr.
> > > > > > 
> > > > > > void *__tls_get_addr(tls_mod_off_t *v)
> > > > > > {
> > > > > >         pthread_t self = __pthread_self();
> > > > > >         return (void *)(self->dtv[v[0]] + v[1]);
> > > > > > }
> > > > > > 
> > > > > > (gdb) disas __tls_get_addr (annotations added by me)
> > > > > > Dump of assembler code for function __tls_get_addr:
> > > > > > r2 = v[0]
> > > > > >    0x0000c940 <+0>: ldr r2, [r0, #0]
> > > > > > r3 = tls
> > > > > >    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> > > > > > self = r3 - 120
> > > > > > r3 = *(r3 - 116) = self->dtv
> > > > > >    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> > > > > > r0 = v[1]
> > > > > >    0x0000c94a <+10>: ldr r0, [r0, #4]
> > > > > > r2 = *(r3 + r2 * 4) = dtv[r2]
> > > > > >    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> > > > > > r0 = r0 + r2
> > > > > >    0x0000c950 <+16>: add r0, r2
> > > > > >    0x0000c952 <+18>: bx lr
> > > > > > End of assembler dump.
> > > > > > (gdb) b __tls_get_addr
> > > > > > 
> > > > > > So after pulling out the values I end up with:
> > > > > > v = 0xf77f000c
> > > > > > 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> > > > > > v[0] = 0
> > > > > > v[1] = 0
> > > > > > self = <builtin_tls> 0xf77f064c
> > > > > > 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> > > > > > self->dtv = <builtin_tls+144> 0xf77f06dc
> > > > > > 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> > > > > > return = dtv[v[0]] + v[1] = 1 + 0 = 1
> > > > > > 
> > > > > > At this point I'm out of my depth.
> > > > > 
> > > > > dtv[0] is special and just stores the length of the dtv
> > > > > (which is correctly 1, meaning there is 1 elf module with tls).
> > > > > 
> > > > > so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
> > > > > the tls address of the executable) i think v[0] should be set
> > > > > up by the static linker so it may be a binutils bug.
> > > > > (or maybe static linking is special and then dtv[0] should be
> > > > > set equal to dtv[1]?)
> > > > 
> > > > MIPS had the exact same bug; I think it's fixed upstream now since we
> > > > had the patch for binutils 2.27 in mcm but dropped it in 2.32:
> > > > 
> > > > https://github.com/richfelker/musl-cross-make/blob/master/patches/binutils-2.27/0004-mips-pie-tls.diff
> > > > 
> > > > It affects static PIE only, so -no-pie should be able to work around
> > > > it (conditional for setting the 1 was essentially !ET_DYN instead of
> > > > is_executable).
> > > > 
> > > > We could work around this in musl in one of two ways: rcrt1 could
> > > > process DTPMOD relocs and write a 1, or the static version of
> > > > __init_tls could just duplicate dtv[1] in dtv[0] (the latter is only
> > > > valid up until we add static-linked dlopen, if we ever do that).
> > > > However I was fairly strongly against doing that for MIPS, since it
> > > > was a binutils bug and only affected static linking (so it's not a
> > > > runtime ABI issue; the binary either works at link-time or it doesn't)
> > > > and I'd lean towards treating ARM the same.
> > > > 
> > > > It should be easy to make and apply the binutils patch to fix this,
> > > > and shouldn't be too hard to grep for the same bug in all archs (since
> > > > binutils nicely duplicates this logic for every single arch, for no
> > > > reason whatsoever).
> > > 
> > > Attached patch is untested but should fix it.
> > 
> > It's missing a second instance and doesn't work. I'll send an updated
> > patch after testing.
> 
> Attached works for me.

> --- binutils-2.32/bfd/elf32-arm.c.orig	2019-10-02 07:47:36.153918869 -0400
> +++ binutils-2.32/bfd/elf32-arm.c	2019-10-02 08:37:09.108263016 -0400
> @@ -11624,7 +11624,7 @@
>  	  {
>  	    /* If we don't know the module number, create a relocation
>  	       for it.  */
> -	    if (bfd_link_pic (info))
> +	    if (bfd_link_dll (info))
>  	      {
>  		Elf_Internal_Rela outrel;
>  
> @@ -11728,7 +11728,7 @@
>  	       now, and emit any relocations.  If both an IE GOT and a
>  	       GD GOT are necessary, we emit the GD first.  */
>  
> -	    if ((bfd_link_pic (info) || indx != 0)
> +	    if ((bfd_link_dll (info) || indx != 0)
>  		&& (h == NULL
>  		    || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
>  			&& !resolved_to_zero)

And, filed bug report with patch:

https://sourceware.org/bugzilla/show_bug.cgi?id=25056

Patches are now in musl-cross-make too.

Rich


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: armv7-m segv when throwing c++ exception
  2019-10-02 14:55           ` Rich Felker
@ 2019-10-02 22:34             ` Patrick Oppenlander
  0 siblings, 0 replies; 8+ messages in thread
From: Patrick Oppenlander @ 2019-10-02 22:34 UTC (permalink / raw)
  To: musl

Wow, that was fast! Thank you!

I really appreciate the effort you & the whole musl community put in.

Patrick

On Thu, Oct 3, 2019 at 12:56 AM Rich Felker <dalias@libc.org> wrote:
>
> On Wed, Oct 02, 2019 at 08:49:12AM -0400, Rich Felker wrote:
> > On Wed, Oct 02, 2019 at 08:25:43AM -0400, Rich Felker wrote:
> > > On Wed, Oct 02, 2019 at 07:49:55AM -0400, Rich Felker wrote:
> > > > On Wed, Oct 02, 2019 at 07:40:45AM -0400, Rich Felker wrote:
> > > > > On Wed, Oct 02, 2019 at 11:22:33AM +0200, Szabolcs Nagy wrote:
> > > > > > * Patrick Oppenlander <patrick.oppenlander@gmail.com> [2019-10-02 12:00:19 +1000]:
> > > > > > > Hi all,
> > > > > > >
> > > > > > > I'm running into an issue with a gcc-8.3.0/musl-1.1.23 toolchain built
> > > > > > > using musl-cross-make for armv7-m.
> > > > > > >
> > > > > > > % cat test.cpp
> > > > > > > int main()
> > > > > > > {
> > > > > > >         throw 0;
> > > > > > > }
> > > > > > > % armv7m-linux-musleabihf-g++ -ggdb -static test.cpp
> > > > > > > % qemu-arm ./a.out
> > > > > > > qemu: uncaught target signal 11 (Segmentation fault) - core dumped
> > > > > > > [2]    198246 segmentation fault (core dumped)  qemu-arm ./a.out
> > > > > > >
> > > > > > > We should be seeing something like "terminate called after throwing an
> > > > > > > instance of 'int'".
> > > > > >
> > > > > > there is at least one known issue in this area:
> > > > > > libgcc uses pthread apis via weak refs so with static
> > > > > > linking they don't get linked in, however i think that
> > > > > > should only affect unwinding in multi-thread processes,
> > > > > > so this is something else
> > > > > >
> > > > > > > I have an ODROID-C2 here which is a convenient place to run gdb:
> > > > > > >
> > > > > > > $ gdb -q ./a.out
> > > > > > > Reading symbols from ./a.out...
> > > > > > > (gdb) run
> > > > > > > Program received signal SIGSEGV, Segmentation fault.
> > > > > > > 0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > > > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > > > > 81 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such
> > > > > > > file or directory.
> > > > > > > (gdb) bt
> > > > > > > #0  0xf77cf9f4 in __cxxabiv1::__cxa_throw (obj=0xf77f5fa0,
> > > > > > > tinfo=0xf77efb20 <typeinfo for int>, dest=0x0)
> > > > > > >     at ../../../../src_gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
> > > > > > > #1  0xf77cf902 in main () at test.cpp:3
> > > > > > > (gdb) disas
> > > > > > > Dump of assembler code for function __cxxabiv1::__cxa_throw(void*,
> > > > > > > std::type_info*, void (*)(void*)):
> > > > > > >    0xf77cf9e0 <+0>: push {r3, r4, r5, r6, r7, lr}
> > > > > > >    0xf77cf9e2 <+2>: mov r5, r0
> > > > > > >    0xf77cf9e4 <+4>: mov r7, r1
> > > > > > >    0xf77cf9e6 <+6>: mov r6, r2
> > > > > > >    0xf77cf9e8 <+8>: bl 0xf77d01c4 <__cxxabiv1::__cxa_get_globals()>
> > > > > > >    0xf77cf9ec <+12>: mov r4, r0
> > > > > > >    0xf77cf9ee <+14>: mov r1, r7
> > > > > > >    0xf77cf9f0 <+16>: mov r2, r6
> > > > > > >    0xf77cf9f2 <+18>: mov r0, r5
> > > > > > > => 0xf77cf9f4 <+20>: ldr r3, [r4, #4]
> > > > > > >    0xf77cf9f6 <+22>: adds r3, #1
> > > > > > >    0xf77cf9f8 <+24>: str r3, [r4, #4]
> > > > > > >    0xf77cf9fa <+26>: bl 0xf77cf984
> > > > > > > <__cxxabiv1::__cxa_init_primary_exception(void*, std::type_info*, void
> > > > > > > (*)(void*))>
> > > > > > >    0xf77cf9fe <+30>: movs r3, #1
> > > > > > >    0xf77cfa00 <+32>: mov r4, r0
> > > > > > >    0xf77cfa02 <+34>: str.w r3, [r4], #40
> > > > > > >    0xf77cfa06 <+38>: mov r0, r4
> > > > > > >    0xf77cfa08 <+40>: bl 0xf77d88a8 <___Unwind_RaiseException>
> > > > > > >    0xf77cfa0c <+44>: mov r0, r4
> > > > > > >    0xf77cfa0e <+46>: bl 0xf77d01e0 <__cxxabiv1::__cxa_begin_catch(void*)>
> > > > > > >    0xf77cfa12 <+50>: bl 0xf77cfaf0 <std::terminate()>
> > > > > > > End of assembler dump.
> > > > > > > (gdb) p/x $r4
> > > > > > > $1 = 0x1
> > > > > > >
> > > > > > > That doesn't look good.
> > > > > > >
> > > > > > > (gdb) disas __cxa_get_globals
> > > > > > > Dump of assembler code for function __cxxabiv1::__cxa_get_globals():
> > > > > > >    0xf77d01c4 <+0>: ldr r0, [pc, #12] ; (0xf77d01d4
> > > > > > > <__cxxabiv1::__cxa_get_globals()+16>)
> > > > > > >    0xf77d01c6 <+2>: push {r3, lr}
> > > > > > >    0xf77d01c8 <+4>: add r0, pc
> > > > > > >    0xf77d01ca <+6>: bl 0xf77da940 <__tls_get_addr>
> > > > > > >    0xf77d01ce <+10>: ldr r3, [pc, #8] ; (0xf77d01d8
> > > > > > > <__cxxabiv1::__cxa_get_globals()+20>)
> > > > > > >    0xf77d01d0 <+12>: add r0, r3
> > > > > > >    0xf77d01d2 <+14>: pop {r3, pc}
> > > > > > >    0xf77d01d4 <+16>: ; <UNDEFINED> instruction: 0001fe40
> > > > > > >    0xf77d01d8 <+20>: ; <UNDEFINED> instruction: 00000000
> > > > > > > End of assembler dump.
> > > > > >
> > > > > > interesting, i thought the linker would relax __tls_get_addr
> > > > > > calls in static linked executables to local-exec access model,
> > > > > > but maybe that's not implemented on arm.
> > > > > >
> > > > > > can you look at the readelf -aW a.out and see if the linker
> > > > > > left any dynamic tls relocs in the exe?
> > > > > >
> > > > > > >
> > > > > > > OK, so what did __tls_get_addr return?
> > > > > > >
> > > > > > > (gdb) b *(__cxa_get_globals + 10)
> > > > > > > Breakpoint 3 at 0xf77d01ce: file
> > > > > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc, line 62.
> > > > > > > (gdb) run
> > > > > > > Breakpoint 3, 0xf77d01ce in __cxxabiv1::__cxa_get_globals () at
> > > > > > > ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc:62
> > > > > > > 62 ../../../../src_gcc/libstdc++-v3/libsupc++/eh_globals.cc: No such
> > > > > > > file or directory.
> > > > > > > (gdb) p/x $r0
> > > > > > > $3 = 0x1
> > > > > > >
> > > > > > > That's doesn't look good either. Let's look at __tls_get_addr.
> > > > > > >
> > > > > > > void *__tls_get_addr(tls_mod_off_t *v)
> > > > > > > {
> > > > > > >         pthread_t self = __pthread_self();
> > > > > > >         return (void *)(self->dtv[v[0]] + v[1]);
> > > > > > > }
> > > > > > >
> > > > > > > (gdb) disas __tls_get_addr (annotations added by me)
> > > > > > > Dump of assembler code for function __tls_get_addr:
> > > > > > > r2 = v[0]
> > > > > > >    0x0000c940 <+0>: ldr r2, [r0, #0]
> > > > > > > r3 = tls
> > > > > > >    0x0000c942 <+2>: ; <UNDEFINED> instruction: 0xee1d3f70
> > > > > > > self = r3 - 120
> > > > > > > r3 = *(r3 - 116) = self->dtv
> > > > > > >    0x0000c946 <+6>: ldr.w r3, [r3, #-116]
> > > > > > > r0 = v[1]
> > > > > > >    0x0000c94a <+10>: ldr r0, [r0, #4]
> > > > > > > r2 = *(r3 + r2 * 4) = dtv[r2]
> > > > > > >    0x0000c94c <+12>: ldr.w r2, [r3, r2, lsl #2]
> > > > > > > r0 = r0 + r2
> > > > > > >    0x0000c950 <+16>: add r0, r2
> > > > > > >    0x0000c952 <+18>: bx lr
> > > > > > > End of assembler dump.
> > > > > > > (gdb) b __tls_get_addr
> > > > > > >
> > > > > > > So after pulling out the values I end up with:
> > > > > > > v = 0xf77f000c
> > > > > > > 0xf77f000c: 0x00000000 0x00000000 0xf77f072c 0xf77eff1c
> > > > > > > v[0] = 0
> > > > > > > v[1] = 0
> > > > > > > self = <builtin_tls> 0xf77f064c
> > > > > > > 0xf77f064c <builtin_tls>: 0xf77f064c 0xf77f06dc 0xf77f064c 0xf77f064c
> > > > > > > self->dtv = <builtin_tls+144> 0xf77f06dc
> > > > > > > 0xf77f06dc <builtin_tls+144>: 0x00000001 0xf77f06cc 0x00000000 0x00000000
> > > > > > > return = dtv[v[0]] + v[1] = 1 + 0 = 1
> > > > > > >
> > > > > > > At this point I'm out of my depth.
> > > > > >
> > > > > > dtv[0] is special and just stores the length of the dtv
> > > > > > (which is correctly 1, meaning there is 1 elf module with tls).
> > > > > >
> > > > > > so the issue is that v[0]==0 instead of 1 (dtv[1] would hold
> > > > > > the tls address of the executable) i think v[0] should be set
> > > > > > up by the static linker so it may be a binutils bug.
> > > > > > (or maybe static linking is special and then dtv[0] should be
> > > > > > set equal to dtv[1]?)
> > > > >
> > > > > MIPS had the exact same bug; I think it's fixed upstream now since we
> > > > > had the patch for binutils 2.27 in mcm but dropped it in 2.32:
> > > > >
> > > > > https://github.com/richfelker/musl-cross-make/blob/master/patches/binutils-2.27/0004-mips-pie-tls.diff
> > > > >
> > > > > It affects static PIE only, so -no-pie should be able to work around
> > > > > it (conditional for setting the 1 was essentially !ET_DYN instead of
> > > > > is_executable).
> > > > >
> > > > > We could work around this in musl in one of two ways: rcrt1 could
> > > > > process DTPMOD relocs and write a 1, or the static version of
> > > > > __init_tls could just duplicate dtv[1] in dtv[0] (the latter is only
> > > > > valid up until we add static-linked dlopen, if we ever do that).
> > > > > However I was fairly strongly against doing that for MIPS, since it
> > > > > was a binutils bug and only affected static linking (so it's not a
> > > > > runtime ABI issue; the binary either works at link-time or it doesn't)
> > > > > and I'd lean towards treating ARM the same.
> > > > >
> > > > > It should be easy to make and apply the binutils patch to fix this,
> > > > > and shouldn't be too hard to grep for the same bug in all archs (since
> > > > > binutils nicely duplicates this logic for every single arch, for no
> > > > > reason whatsoever).
> > > >
> > > > Attached patch is untested but should fix it.
> > >
> > > It's missing a second instance and doesn't work. I'll send an updated
> > > patch after testing.
> >
> > Attached works for me.
>
> > --- binutils-2.32/bfd/elf32-arm.c.orig        2019-10-02 07:47:36.153918869 -0400
> > +++ binutils-2.32/bfd/elf32-arm.c     2019-10-02 08:37:09.108263016 -0400
> > @@ -11624,7 +11624,7 @@
> >         {
> >           /* If we don't know the module number, create a relocation
> >              for it.  */
> > -         if (bfd_link_pic (info))
> > +         if (bfd_link_dll (info))
> >             {
> >               Elf_Internal_Rela outrel;
> >
> > @@ -11728,7 +11728,7 @@
> >              now, and emit any relocations.  If both an IE GOT and a
> >              GD GOT are necessary, we emit the GD first.  */
> >
> > -         if ((bfd_link_pic (info) || indx != 0)
> > +         if ((bfd_link_dll (info) || indx != 0)
> >               && (h == NULL
> >                   || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
> >                       && !resolved_to_zero)
>
> And, filed bug report with patch:
>
> https://sourceware.org/bugzilla/show_bug.cgi?id=25056
>
> Patches are now in musl-cross-make too.
>
> Rich


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-10-02 22:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-02  2:00 armv7-m segv when throwing c++ exception Patrick Oppenlander
2019-10-02  9:22 ` Szabolcs Nagy
2019-10-02 11:40   ` Rich Felker
2019-10-02 11:49     ` Rich Felker
2019-10-02 12:25       ` Rich Felker
2019-10-02 12:49         ` Rich Felker
2019-10-02 14:55           ` Rich Felker
2019-10-02 22:34             ` Patrick Oppenlander

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).