psmedley / gcc

GNU General Public License v2.0
7 stars 1 forks source link

Forked children cause assertion in libgcc #25

Open dmik opened 7 years ago

dmik commented 7 years ago

While playing with libc066.logchk DLL, I found that GCC1.DLL causes an assertion (most likely through gcc_assert) during the forked child termination. The test program is attached. This is the call stack (EXCEPTQ install is needed together with LIBC_BREAKPOINT_ABORT=1 to see that, it's omitted in the test):

______________________________________________________________________

 Call Stack
______________________________________________________________________

   EBP     Address    Module     Obj:Offset    Nearest Public Symbol
 --------  ---------  --------  -------------  -----------------------
 Trap  ->  1F6EAC82   LIBC066   0001:0004AC82

 0012FE98  1F697EC1   GCC1      0001:00007EC1

 0012FEC8  1F697EF9   GCC1      0001:00007EF9

 0012FEE8  000114F2   TST-MSYN  0001:000014F2  emx-eh.c#40 ___ehTerm + 32 0001:000014C0 (emx-eh.obj)

 0012FF08  1D666038   LIBC066   0001:00036038  dtor1.c#18 ___ctordtorTerm1 + 90 0001:00035FA8 (dtor1.obj)

  Offset Name                 Type                         Hex Value
  ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ
   8     ptr                  pointer to 32 bit signed     201B4

 0012FF28  00011475   TST-MSYN  0001:00001475  emx-ctordtor.c#51 ___ctordtorTerm + 35 0001:00001440 (emx-ctordtor.obj)

 0012FF48  1D69AEA8   LIBC066   0001:0006AEA8  exit.c#69 __std_exit + C0 0001:0006ADE8 (exit.obj)

  Offset Name                 Type                         Hex Value
  ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ
   8     ret                  32 bit signed                0

 0012FF68  00010030   TST-MSYN  0001:00000030  crt0.s#94 __text + 30 0001:00000000 ({standard input})

 0012FF8C  1D628011   LIBCX0    0001:00008011  main.s#45 ___init_app + 11 0001:00008000 (D:\Temp\cctGx8Cw.s)

 0012FFE0  1D66945B   LIBC066   0001:0003945B  appinit.s#16 ___init_app + B 0001:00039450 (appinit.obj)

______________________________________________________________________

The above addresses in GCC1 are most likely __deregister_frame and __deregister_frame_info (which calls __deregister_frame_info_bases which calls gcc_assert which calls abort which issues int 3 if LIBC_BREAKPOINT_ABORT is set).

Although emx-ctordtor.c contains some fork callbacks, it looks like the constructor/destructor chain is not properly aligned in the forked child so it asserts on a NULL pointer (presumably, libgcc\unwind-dw2-fde.c line 216, a debug of GCC with symbols s necessary to tell more).

dmik commented 7 years ago

This is a test case:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <wait.h>

int main()
{
  int status;

  printf("Parent\n");

  switch (fork())
  {
    case -1:
      perror("fork failed");
      exit(-1);

    case 0:
    {
      printf("Child\n");
      return 0;
    }
  }

  if (wait(&status) == -1)
  {
    perror("wait failed");
    return 1;
  }
  if (!WIFEXITED(status) || WEXITSTATUS(status))
  {
    printf("child crashed or returned non-zero (status %x)\n", status);
    return 1;
  }

  return 0;
}
psmedley commented 7 years ago

With GCC 6.1.0, I get: [U:\DEV]gcc -Zexe test.c [U:\DEV]test Parent Child

dmik commented 7 years ago

Well, did you try the LOGCHK version of LIBC with that?

psmedley commented 7 years ago

Yup.... [u:\dev]copy e:\usr\lib\libc066.logchk libc066.dll E:\usr\lib\libc066.logchk => U:\DEV\libc066.dll 1 file copied

[u:\dev]copy c:\PROGRAMS\firefox\firefox!.exe test!l.exe C:\PROGRAMS\firefox\firefox!.exe => U:\DEV\test!l.exe 1 file copied

[u:\dev]test!l.exe

[u:\dev]test!l.exe 2>&1 | tee log Parent Child Parent

dmik commented 7 years ago

Well, it's not enough to copy it over, you need BEGINLIBPATH/LIBPATHSTRICT=T (since there is a normal copy of LIBC066 in memory which will always be used otherwise).

psmedley commented 7 years ago

yep, hence the use of run! to start it in a new session...

dmik commented 7 years ago

That's strange. Can you try GCC 4.9.2 instead?

psmedley commented 7 years ago

gcc 4.9.2 gives the same results here. How are you compiling the example. Optimisation related?

dmik commented 7 years ago

Nothing special, just gcc -Zomf test.c. But ok, it turns out that the reason is that I have a slightly different LIBC LOGCHK build than the RPM one — there I have different names for LIBC shared mem and mutex names because otherwise it tries to write to use the structures of the already loaded normal LIBC and it screws some things up like fork() under some circumstances. So using a completely different build somehow seems to screw the GCC DLL. I need to do a few other tests to decide what to do with this.