5#include <private/qcore_mac_p.h>
8#include <AppKit/AppKit.h>
11#if defined(QT_PLATFORM_UIKIT)
12#include <UIKit/UIKit.h>
19#include <objc/runtime.h>
20#include <mach-o/dyld.h>
21#include <sys/sysctl.h>
30#include "private/qlocking_p.h"
32#if !defined(QT_BOOTSTRAPPED)
36#if !defined(QT_APPLE_NO_PRIVATE_APIS)
41#ifdef QT_BUILD_INTERNAL
42int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs,
int disclaim)
43__attribute__((availability(macos,introduced=10.14),weak_import));
44pid_t responsibility_get_pid_responsible_for_pid(pid_t)
__attribute__((weak_import));
53using namespace Qt::StringLiterals;
57#if defined(Q_OS_MACOS)
58static void initializeStandardUserDefaults()
67 Q_UNUSED(NSUserDefaults.standardUserDefaults);
69Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
74QCFString::operator QString()
const
76 if (string.isEmpty() && value)
77 const_cast<QCFString*>(
this)->string = QString::fromCFString(value);
81QCFString::operator CFStringRef()
const
84 const_cast<QCFString*>(
this)->value = string.toCFString();
90#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
92bool AppleUnifiedLogger::preventsStderrLogging()
112 static bool willMirror = qEnvironmentVariableIsSet(
"OS_ACTIVITY_DT_MODE")
113 || qEnvironmentVariableIsSet(
"ACTIVITY_LOG_STDERR")
114 || qEnvironmentVariableIsSet(
"CFLOG_FORCE_STDERR");
118 static bool disableStderr = qEnvironmentVariableIsSet(
"CFLOG_FORCE_DISABLE_STDERR");
120 return willMirror || disableStderr;
123QT_MAC_WEAK_IMPORT(_os_log_default);
124bool AppleUnifiedLogger::messageHandler(QtMsgType msgType,
const QMessageLogContext &context,
125 const QString &message,
const QString &optionalSubsystem)
127 QString subsystem = optionalSubsystem;
128 if (subsystem.isNull()) {
129 static QString bundleIdentifier = []() {
130 if (CFBundleRef bundle = CFBundleGetMainBundle()) {
131 if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
132 return QString::fromCFString(identifier);
136 subsystem = bundleIdentifier;
139 const bool isDefault = !context.category || !strcmp(context.category,
"default");
140 os_log_t log = isDefault ? OS_LOG_DEFAULT :
141 os_log_create(subsystem.toLatin1().constData(), context.category);
142 os_log_type_t logType = logTypeForMessageType(msgType);
144 if (!os_log_type_enabled(log, logType))
159 os_log_with_type(log, logType,
"%{public}s", qPrintable(message));
161 return preventsStderrLogging();
164os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
167 case QtDebugMsg:
return OS_LOG_TYPE_DEBUG;
168 case QtInfoMsg:
return OS_LOG_TYPE_INFO;
169 case QtWarningMsg:
return OS_LOG_TYPE_DEFAULT;
170 case QtCriticalMsg:
return OS_LOG_TYPE_ERROR;
171 case QtFatalMsg:
return OS_LOG_TYPE_FAULT;
174 return OS_LOG_TYPE_DEFAULT;
179#ifndef QT_NO_DEBUG_STREAM
190 for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
191 if (cls == NSObject.
class) {
192 dbg <<
static_cast<NSObject*>(obj);
198 const QDebugStateSaver saver(dbg);
199 dbg.nospace() <<
'<' << object_getClassName(obj) <<
": " <<
static_cast<
void*>(obj) <<
'>';
205 return dbg << (nsObject ?
206 dbg.verbosity() > 2 ?
207 nsObject.debugDescription.UTF8String :
208 nsObject.description.UTF8String
215 return dbg <<
"CFStringRef(0x0)";
217 if (
const UniChar *chars = CFStringGetCharactersPtr(stringRef))
218 dbg << QStringView(
reinterpret_cast<
const QChar *>(chars), CFStringGetLength(stringRef));
220 dbg << QString::fromCFString(stringRef);
227#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
228 __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
242@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
245@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
247QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
262QMacAutoReleasePool::QMacAutoReleasePool()
263 : pool(objc_autoreleasePoolPush())
266 static const bool debugAutoReleasePools = qEnvironmentVariableIsSet(
"QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
267 if (!debugAutoReleasePools)
270 Class trackerClass = [QMacAutoReleasePoolTracker
class];
272 void *poolFrame =
nullptr;
274 if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
275 poolFrame = frames[1];
279 if (dladdr(poolFrame, &info) && info.dli_sname) {
280 const char *symbolName = info.dli_sname;
281 if (symbolName[0] ==
'_') {
283 if (
char *demangled = abi::__cxa_demangle(info.dli_sname,
nullptr, 0, &status))
284 symbolName = demangled;
287 char *className =
nullptr;
288 asprintf(&className,
" ^-- allocated in function: %s", symbolName);
290 if (Class existingClass = objc_getClass(className))
291 trackerClass = existingClass;
293 trackerClass = objc_duplicateClass(trackerClass, className, 0);
297 if (symbolName != info.dli_sname)
298 free((
char*)symbolName);
302 [[trackerClass
new] autorelease];
306QMacAutoReleasePool::~QMacAutoReleasePool()
308 objc_autoreleasePoolPop(pool);
311#ifndef QT_NO_DEBUG_STREAM
314 QDebugStateSaver saver(debug);
316 debug <<
"QMacAutoReleasePool(" << (
const void *)pool <<
')';
322 debug <<
static_cast<QString>(string);
327#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
328bool qt_mac_runningUnderRosetta()
331 auto size =
sizeof(translated);
332 if (sysctlbyname(
"sysctl.proc_translated", &translated, &size,
nullptr, 0) == 0)
337bool qt_apple_runningWithLiquidGlass()
339 static const bool runningWithLiquidGlass = []{
340 if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary).majorVersion() < 26)
343 if (QMacVersion::currentRuntime().majorVersion() < 26)
350 if (QMacVersion::currentRuntime().majorVersion() < 27) {
351 const id liquidGlassOptOut = [NSBundle.mainBundle
352 objectForInfoDictionaryKey:@
"UIDesignRequiresCompatibility"];
353 if (liquidGlassOptOut && [liquidGlassOptOut boolValue])
360 return runningWithLiquidGlass;
363std::optional<uint32_t> qt_mac_sipConfiguration()
365 static auto configuration = []() -> std::optional<uint32_t> {
366#if !defined(QT_APPLE_NO_PRIVATE_APIS)
368 if (csr_get_active_config && csr_get_active_config(&config) == 0)
372 QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault,
"IODeviceTree:/options");
374 qWarning(
"Failed to locate NVRAM entry in IO registry");
378 QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
379 CFSTR(
"csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
383 if (
auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
384#ifndef QT_NO_DEBUG_STREAM
385 qWarning() <<
"Unexpected SIP config type" << CFCopyTypeIDDescription(type);
390 QByteArray data = QByteArray::fromRawCFData(csrConfig.as<CFDataRef>());
391 if (data.size() !=
sizeof(uint32_t)) {
392 qWarning(
"Unexpected SIP config size %td", ptrdiff_t(data.size()));
396 return qFromLittleEndian<uint32_t>(data.constData());
398 return configuration;
401bool qt_mac_processHasEntitlement(
const QString &entitlement)
403 if (QCFType<SecTaskRef> task = SecTaskCreateFromSelf(kCFAllocatorDefault)) {
404 if (QCFType<CFTypeRef> value = SecTaskCopyValueForEntitlement(task,
405 entitlement.toCFString(),
nullptr)) {
407 if (CFGetTypeID(value) != CFBooleanGetTypeID())
410 return CFBooleanGetValue(value.as<CFBooleanRef>());
416#define CHECK_SPAWN(expr)
418 posix_spawnattr_destroy(&attr);
422#ifdef QT_BUILD_INTERNAL
423void qt_mac_ensureResponsible()
425#if !defined(QT_APPLE_NO_PRIVATE_APIS)
426 if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
430 if (responsibility_get_pid_responsible_for_pid(pid) == pid)
433 posix_spawnattr_t attr = {};
434 CHECK_SPAWN(posix_spawnattr_init(&attr));
437 short flags = POSIX_SPAWN_SETEXEC;
441 sigemptyset(&no_signals);
442 CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
443 flags |= POSIX_SPAWN_SETSIGMASK;
446 sigset_t all_signals;
447 sigfillset(&all_signals);
448 CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
449 flags |= POSIX_SPAWN_SETSIGDEF;
451 CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
453 CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
455 char **argv = *_NSGetArgv();
456 posix_spawnp(&pid, argv[0],
nullptr, &attr, argv, environ);
457 posix_spawnattr_destroy(&attr);
466 static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@
"NSExtension"];
470#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
475 qWarning() <<
"accessing the shared" << [AppleApplication
class]
476 <<
"is not allowed in application extensions";
481#if QT_CONFIG(appstore_compliant)
488 return [[AppleApplication
class] performSelector:@selector(sharedApplication)];
492#if !defined(QT_BOOTSTRAPPED)
496#if defined(Q_OS_MACOS)
497 static bool isSandboxed = qt_mac_processHasEntitlement(u"com.apple.security.app-sandbox"_s);
505@implementation NSObject (QtExtras)
506- (id)qt_valueForPrivateKey:(NSString *)key
508 if (qt_apple_isSandboxed())
511 return [self valueForKey:key];
519
520
521
522
523
524
527#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE
528@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end
529@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end
530QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER);
533const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] =
"QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
535QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
537 if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
542 [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
544 if (qstrcmp(qgetenv(
"OBJC_DEBUG_MISSING_POOLS"),
"YES") == 0) {
545 qDebug(
"QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n"
546 "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s",
547 __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH);
551QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
558#ifndef QT_BOOTSTRAPPED
559void qt_apple_check_os_version()
561#if defined(__WATCH_OS_VERSION_MIN_REQUIRED)
562 const char *os =
"watchOS";
563 const int version = __WATCH_OS_VERSION_MIN_REQUIRED;
564#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
565 const char *os =
"tvOS";
566 const int version = __TV_OS_VERSION_MIN_REQUIRED;
567#elif defined(__VISION_OS_VERSION_MIN_REQUIRED)
568 const char *os =
"visionOS";
569 const int version = __VISION_OS_VERSION_MIN_REQUIRED;
570#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
571 const char *os =
"iOS";
572 const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
573#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
574 const char *os =
"macOS";
575 const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
578 const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
579 const auto current = QOperatingSystemVersion::current().version();
581#if defined(Q_OS_MACOS)
584 if (current.majorVersion() == 10 && current.minorVersion() >= 16)
588 if (current < required) {
589 NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
590 NSString *applicationName = plist[@
"CFBundleDisplayName"];
591 if (!applicationName)
592 applicationName = plist[@
"CFBundleName"];
593 if (!applicationName)
594 applicationName = NSProcessInfo.processInfo.processName;
596 fprintf(stderr,
"Sorry, \"%s\" cannot be run on this version of %s. "
597 "Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n",
598 applicationName.UTF8String, os,
599 os,
long(required.majorVersion()),
long(required.minorVersion()),
long(required.microVersion()),
600 os,
long(current.majorVersion()),
long(current.minorVersion()),
long(current.microVersion()));
610void QMacNotificationObserver::remove()
613 [[NSNotificationCenter defaultCenter] removeObserver:observer];
619QMacKeyValueObserver::QMacKeyValueObserver(
const QMacKeyValueObserver &other)
620 : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get())
624void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
626 [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
629void QMacKeyValueObserver::removeObserver() {
631 [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
635KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
638@implementation QT_MANGLE_NAMESPACE(KeyValueObserver)
639- (
void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
640 change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(
void *)context
646 (*
reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
653#ifndef QT_BOOTSTRAPPED
654QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
657 case ApplicationBinary:
return applicationVersion().second;
658 case QtLibraries:
return libraryVersion().second;
663QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
666 case ApplicationBinary:
return applicationVersion().first;
667 case QtLibraries:
return libraryVersion().first;
672QOperatingSystemVersion QMacVersion::currentRuntime()
674 return QOperatingSystemVersion::current();
690QMacVersion::VersionTuple QMacVersion::versionsForImage(
const mach_header *machHeader)
692 static auto osForLoadCommand = [](uint32_t cmd) {
694 case LC_VERSION_MIN_MACOSX:
return QOperatingSystemVersion::MacOS;
695 case LC_VERSION_MIN_IPHONEOS:
return QOperatingSystemVersion::IOS;
696 case LC_VERSION_MIN_TVOS:
return QOperatingSystemVersion::TvOS;
697 case LC_VERSION_MIN_WATCHOS:
return QOperatingSystemVersion::WatchOS;
698 default:
return QOperatingSystemVersion::Unknown;
702 static auto osForPlatform = [](uint32_t platform) {
704 case Platform::macOS:
705 return QOperatingSystemVersion::MacOS;
707 case Platform::iOSSimulator:
708 return QOperatingSystemVersion::IOS;
710 case Platform::tvOSSimulator:
711 return QOperatingSystemVersion::TvOS;
712 case Platform::watchOS:
713 case Platform::watchOSSimulator:
714 return QOperatingSystemVersion::WatchOS;
716 return QOperatingSystemVersion::Unknown;
720 static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
722 QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
723 QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
727 const bool is64Bit = machHeader->magic == MH_MAGIC_64 || machHeader->magic == MH_CIGAM_64;
728 auto commandCursor = uintptr_t(machHeader) + (is64Bit ?
sizeof(mach_header_64) :
sizeof(mach_header));
730 for (uint32_t i = 0; i < machHeader->ncmds; ++i) {
731 load_command *loadCommand =
reinterpret_cast<load_command *>(commandCursor);
732 if (loadCommand->cmd == LC_VERSION_MIN_MACOSX || loadCommand->cmd == LC_VERSION_MIN_IPHONEOS
733 || loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
734 auto versionCommand =
reinterpret_cast<version_min_command *>(loadCommand);
735 return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
736 }
else if (loadCommand->cmd == LC_BUILD_VERSION) {
737 auto versionCommand =
reinterpret_cast<build_version_command *>(loadCommand);
738 return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
740 commandCursor += loadCommand->cmdsize;
742 Q_ASSERT_X(
false,
"QMacVersion",
"Could not find any version load command");
746QMacVersion::VersionTuple QMacVersion::applicationVersion()
748 static VersionTuple version = []() {
749 const mach_header *executableHeader =
nullptr;
750 for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
751 auto header = _dyld_get_image_header(i);
752 if (header->filetype == MH_EXECUTE) {
753 executableHeader = header;
757 Q_ASSERT_X(executableHeader,
"QMacVersion",
"Failed to resolve Mach-O header of executable");
758 return versionsForImage(executableHeader);
763QMacVersion::VersionTuple QMacVersion::libraryVersion()
765 static VersionTuple version = []() {
767 dladdr((
const void *)&QMacVersion::libraryVersion, &qtCoreImage);
768 Q_ASSERT_X(qtCoreImage.dli_fbase,
"QMacVersion",
"Failed to resolve Mach-O header of QtCore");
769 return versionsForImage(
static_cast<mach_header*>(qtCoreImage.dli_fbase));
777QObjCWeakPointerBase::QObjCWeakPointerBase(NSObject *object)
778 : m_weakReference(object)
782QObjCWeakPointerBase::QObjCWeakPointerBase(
const QObjCWeakPointerBase &other)
784 QMacAutoReleasePool pool;
785 m_weakReference = other.m_weakReference;
788QObjCWeakPointerBase &QObjCWeakPointerBase::operator=(
const QObjCWeakPointerBase &other)
790 QMacAutoReleasePool pool;
791 m_weakReference = other.m_weakReference;
795QObjCWeakPointerBase::~QObjCWeakPointerBase()
797 QMacAutoReleasePool pool;
798 m_weakReference = nil;
801NSObject *QObjCWeakPointerBase::get()
const
808 return m_weakReference;
QT_FOR_EACH_CORE_FOUNDATION_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE)
int csr_get_active_config(csr_config_t *) __attribute__((weak_import))
void objc_autoreleasePoolPop(void *pool)
QT_END_NAMESPACE QT_USE_NAMESPACE void * objc_autoreleasePoolPush(void)
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
AppleApplication * qt_apple_sharedApplication()
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
Q_CORE_EXPORT bool qt_apple_isApplicationExtension()
Q_CORE_EXPORT bool qt_apple_isSandboxed()
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)