Skip to content

This below was reported as a Qt bug 139361.

Found the Problem

Compiler mismatch with Posix

In narrowing down the problem to a certain version, the cause is probably related to a used toolchain. I've got 2 versions of v6.9.1, and one is broken. At first glance it looks like a GNU toolchain mismatch.

The cross-compiler on Linux seems to be posix one and the one I used on Windows a non-posix. Also the version on Linux was according the package a 13.2.

g++ --version (is broken)

g++.exe (GCC) 13.1.0 Copyright (C) 2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

g++ --version on Qt provided compiler (is working)

g++.exe (x86_64-posix-seh-rev1, Built by MinGW-Builds project) 13.1.0 Copyright (C) 2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Compiler version mismatch

Linux MinGW version is apparently v13.0.0 which is not a released version for Windows. The Qt framework I built using Windows MinGW v13.2.0. Before this was not a problem since stdc++-6.dll is designed to be forward compatible.
Still when starting the executable from Windows it could not link _ZSt21ios_base_library_initv from stdc++-6.dll as where Wine could. (A Wine dynamic linker workaround used in is presumed). It looks like the issue GCC Bugzilla – Bug 116159 is the cause of it all and seems to be solved in GCC v14.3 which is not available yet.

Custom Qt v6.9.1 causes SIGSEGV on exit

Context

I build the Qt Library (https://code.qt.io/qt/qt5.git tag: v6.9.1) myself for Ubuntu Linux (x86_64, aarch64) and Windows (x86_64) to include single Docker image to build all 3 targeted OS/architectures in a single CI/CD pipeline using a single Docker image.

The Windows app is cross-compiled from Linux using mingw and unittested using Wine from the Docker container as well.

The Windows Qt Framework is built natively on Windows 11.

Not the full Qt framework is built to keep small and thus the Docker image.
(see attachment: config.summary)

Issue

After upgrading a working application from Qt v6.8.1 to v6.9.1 the application hung on exit in a destructor from a thread called "Thread(pooled)":

set_thread_data(QThreadData*)::Cleanup::~Cleanup()

Debugging Disassembly

set_thread_data(QThreadData*)::Cleanup::~Cleanup():
  endbr64
  push   %rsi
  push   %rbx
  sub    $0x28,%rsp
  lea    0x27762f(%rip),%rcx        # 0x7fffe7a292e0 <__emutls_v._ZL17currentThreadData>
  call   0x7fffe7947570 <__emutls_get_address>
  mov    (%rax),%rbx
  mov    %rax,%rsi

SIGSEGV signal on the next 'mov' instruction where '$rbx' seems to be a nullptr or uninitialized.

  mov    0x50(%rbx),%rax
  cmpb   $0x0,0x8e(%rbx)
  jne    0x7fffe77b1ce8 <set_thread_data(QThreadData*)::Cleanup::~Cleanup()+72>
  lock subl $0x1,0x80(%rbx)
  je     0x7fffe77b1d00 <set_thread_data(QThreadData*)::Cleanup::~Cleanup()+96>
  movq   $0x0,(%rsi)
  add    $0x28,%rsp
  pop    %rbx
  pop    %rsi
  ret
  nopl   0x0(%rax)
  mov    0x8(%rax),%rcx
  mov    $0x1,%edx
  call   0x7fffe77b19e0 <QThreadPrivate::finish(bool)>
  jmp    0x7fffe77b1cc9 <set_thread_data(QThreadData*)::Cleanup::~Cleanup()+41>
  nopl   0x0(%rax,%rax,1)
  mov    %rbx,%rcx
  call   0x7fffe76f7560 <QThreadData::~QThreadData()>
  mov    $0x90,%edx
  mov    %rbx,%rcx
  call   0x7fffe79461b0 <operator delete(void*, unsigned long long)>
  movq   $0x0,(%rsi)
  add    $0x28,%rsp
  pop    %rbx
  pop    %rsi
  ret
  nop
  data16 cs nopw 0x0(%rax,%rax,1)
  nop 

File: qtbase/src/corelib/qthread_win.cpp:86

static void set_thread_data(QThreadData *data) noexcept
{
    if (data) {
        struct Cleanup {
            Cleanup() { QThreadStoragePrivate::init(); }
            ~Cleanup() { destroy_current_thread_data(currentThreadData); }
        };
        static thread_local Cleanup currentThreadCleanup;
    }
    currentThreadData = data;
} 

Isolation Steps

1) Single File Test Application

I isolated the problem a created a simple application causing the problem when a QPushButton instance not parented by a QWidget.

#include <QApplication>
#include <QDebug>
#include <QPushButton>

int main(int argc, char* argv[])
{
  int rv{0};
  QApplication app(argc, argv);
  QWidget* wgt{nullptr};
  if (argc == 1)
   // Makes it hang when application exists. When debugging causes a SIGSEGV signal.
   wgt = new QPushButton(QString(qVersion()) + "/" + QT_VERSION_STR);
  else
   // Seems to be no problem.
   wgt = new QWidget();
  wgt->setWindowTitle("Qt Test Application");
  wgt->resize(300, 120);
  wgt->show();
  rv = app.exec();
  delete wgt;
  qDebug() << "Exiting with code:" << rv;
  return rv;
}

2) Compiler

To be sure the compiler MinGW-w64-x86_64-13.1.0-release-posix-seh-msvcrt-rt_v11-rev1.7z was downloaded from Qt to build the framework.

3) Using Pre-build Windows Qt v6.9.1 Framework

To narrow the problem down I used the downloaded the prebuild Qt v6.9.1 full framework.
This does NOT seem to cause the issue.

Reproduce

To build the Qt framework I use a single bash script build-qt-lib.sh (qt-v6.9.1-core-bug.zip) also used from Windows in a Cygwin environment.

My Conclusion

Although I have not compiled the complete framework myself (takes a really long time) it seems omitting components introduces a bug that version v6.8.1 did not have using the same components building the framework subset.