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));
55#if defined(Q_OS_MACOS)
56static void initializeStandardUserDefaults()
65 Q_UNUSED(NSUserDefaults.standardUserDefaults);
67Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
72QCFString::operator QString()
const
74 if (string.isEmpty() && value)
75 const_cast<QCFString*>(
this)->string = QString::fromCFString(value);
79QCFString::operator CFStringRef()
const
82 const_cast<QCFString*>(
this)->value = string.toCFString();
88#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
90bool AppleUnifiedLogger::preventsStderrLogging()
110 static bool willMirror = qEnvironmentVariableIsSet(
"OS_ACTIVITY_DT_MODE")
111 || qEnvironmentVariableIsSet(
"ACTIVITY_LOG_STDERR")
112 || qEnvironmentVariableIsSet(
"CFLOG_FORCE_STDERR");
116 static bool disableStderr = qEnvironmentVariableIsSet(
"CFLOG_FORCE_DISABLE_STDERR");
118 return willMirror || disableStderr;
121QT_MAC_WEAK_IMPORT(_os_log_default);
122bool AppleUnifiedLogger::messageHandler(QtMsgType msgType,
const QMessageLogContext &context,
123 const QString &message,
const QString &optionalSubsystem)
125 QString subsystem = optionalSubsystem;
126 if (subsystem.isNull()) {
127 static QString bundleIdentifier = []() {
128 if (CFBundleRef bundle = CFBundleGetMainBundle()) {
129 if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
130 return QString::fromCFString(identifier);
134 subsystem = bundleIdentifier;
137 const bool isDefault = !context.category || !strcmp(context.category,
"default");
138 os_log_t log = isDefault ? OS_LOG_DEFAULT :
139 os_log_create(subsystem.toLatin1().constData(), context.category);
140 os_log_type_t logType = logTypeForMessageType(msgType);
142 if (!os_log_type_enabled(log, logType))
157 os_log_with_type(log, logType,
"%{public}s", qPrintable(message));
159 return preventsStderrLogging();
162os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
165 case QtDebugMsg:
return OS_LOG_TYPE_DEBUG;
166 case QtInfoMsg:
return OS_LOG_TYPE_INFO;
167 case QtWarningMsg:
return OS_LOG_TYPE_DEFAULT;
168 case QtCriticalMsg:
return OS_LOG_TYPE_ERROR;
169 case QtFatalMsg:
return OS_LOG_TYPE_FAULT;
172 return OS_LOG_TYPE_DEFAULT;
177#ifndef QT_NO_DEBUG_STREAM
188 for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
189 if (cls == NSObject.
class) {
190 dbg <<
static_cast<NSObject*>(obj);
196 const QDebugStateSaver saver(dbg);
197 dbg.nospace() <<
'<' << object_getClassName(obj) <<
": " <<
static_cast<
void*>(obj) <<
'>';
203 return dbg << (nsObject ?
204 dbg.verbosity() > 2 ?
205 nsObject.debugDescription.UTF8String :
206 nsObject.description.UTF8String
213 return dbg <<
"CFStringRef(0x0)";
215 if (
const UniChar *chars = CFStringGetCharactersPtr(stringRef))
216 dbg << QStringView(
reinterpret_cast<
const QChar *>(chars), CFStringGetLength(stringRef));
218 dbg << QString::fromCFString(stringRef);
225#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
226 __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
240@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
243@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
245QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
260QMacAutoReleasePool::QMacAutoReleasePool()
261 : pool(objc_autoreleasePoolPush())
264 static const bool debugAutoReleasePools = qEnvironmentVariableIsSet(
"QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
265 if (!debugAutoReleasePools)
268 Class trackerClass = [QMacAutoReleasePoolTracker
class];
270 void *poolFrame =
nullptr;
272 if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
273 poolFrame = frames[1];
277 if (dladdr(poolFrame, &info) && info.dli_sname) {
278 const char *symbolName = info.dli_sname;
279 if (symbolName[0] ==
'_') {
281 if (
char *demangled = abi::__cxa_demangle(info.dli_sname,
nullptr, 0, &status))
282 symbolName = demangled;
285 char *className =
nullptr;
286 asprintf(&className,
" ^-- allocated in function: %s", symbolName);
288 if (Class existingClass = objc_getClass(className))
289 trackerClass = existingClass;
291 trackerClass = objc_duplicateClass(trackerClass, className, 0);
295 if (symbolName != info.dli_sname)
296 free((
char*)symbolName);
300 [[trackerClass
new] autorelease];
304QMacAutoReleasePool::~QMacAutoReleasePool()
306 objc_autoreleasePoolPop(pool);
309#ifndef QT_NO_DEBUG_STREAM
312 QDebugStateSaver saver(debug);
314 debug <<
"QMacAutoReleasePool(" << (
const void *)pool <<
')';
320 debug <<
static_cast<QString>(string);
325#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
326bool qt_mac_runningUnderRosetta()
329 auto size =
sizeof(translated);
330 if (sysctlbyname(
"sysctl.proc_translated", &translated, &size,
nullptr, 0) == 0)
335bool qt_apple_runningWithLiquidGlass()
337 static const bool runningWithLiquidGlass = []{
338 if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary).majorVersion() < 26)
341 if (QMacVersion::currentRuntime().majorVersion() < 26)
348 if (QMacVersion::currentRuntime().majorVersion() < 27) {
349 const id liquidGlassOptOut = [NSBundle.mainBundle
350 objectForInfoDictionaryKey:@
"UIDesignRequiresCompatibility"];
351 if (liquidGlassOptOut && [liquidGlassOptOut boolValue])
358 return runningWithLiquidGlass;
361std::optional<uint32_t> qt_mac_sipConfiguration()
363 static auto configuration = []() -> std::optional<uint32_t> {
364#if !defined(QT_APPLE_NO_PRIVATE_APIS)
366 if (csr_get_active_config && csr_get_active_config(&config) == 0)
370 QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault,
"IODeviceTree:/options");
372 qWarning(
"Failed to locate NVRAM entry in IO registry");
376 QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
377 CFSTR(
"csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
381 if (
auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
382#ifndef QT_NO_DEBUG_STREAM
383 qWarning() <<
"Unexpected SIP config type" << CFCopyTypeIDDescription(type);
388 QByteArray data = QByteArray::fromRawCFData(csrConfig.as<CFDataRef>());
389 if (data.size() !=
sizeof(uint32_t)) {
390 qWarning(
"Unexpected SIP config size %td", ptrdiff_t(data.size()));
394 return qFromLittleEndian<uint32_t>(data.constData());
396 return configuration;
399#define CHECK_SPAWN(expr)
401 posix_spawnattr_destroy(&attr);
405#ifdef QT_BUILD_INTERNAL
406void qt_mac_ensureResponsible()
408#if !defined(QT_APPLE_NO_PRIVATE_APIS)
409 if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
413 if (responsibility_get_pid_responsible_for_pid(pid) == pid)
416 posix_spawnattr_t attr = {};
417 CHECK_SPAWN(posix_spawnattr_init(&attr));
420 short flags = POSIX_SPAWN_SETEXEC;
424 sigemptyset(&no_signals);
425 CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
426 flags |= POSIX_SPAWN_SETSIGMASK;
429 sigset_t all_signals;
430 sigfillset(&all_signals);
431 CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
432 flags |= POSIX_SPAWN_SETSIGDEF;
434 CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
436 CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
438 char **argv = *_NSGetArgv();
439 posix_spawnp(&pid, argv[0],
nullptr, &attr, argv, environ);
440 posix_spawnattr_destroy(&attr);
449 static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@
"NSExtension"];
453#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
458 qWarning() <<
"accessing the shared" << [AppleApplication
class]
459 <<
"is not allowed in application extensions";
464#if QT_CONFIG(appstore_compliant)
471 return [[AppleApplication
class] performSelector:@selector(sharedApplication)];
475#if !defined(QT_BOOTSTRAPPED)
477#if defined(Q_OS_MACOS)
481 SandboxChecker() : m_thread([
this]{
483 QCFType<SecStaticCodeRef> staticCode =
nullptr;
484 NSURL *executableUrl = NSBundle.mainBundle.executableURL;
485 if (SecStaticCodeCreateWithPath((__bridge CFURLRef)executableUrl,
486 kSecCSDefaultFlags, &staticCode) != errSecSuccess)
489 QCFType<SecRequirementRef> sandboxRequirement;
490 if (SecRequirementCreateWithString(CFSTR(
"entitlement[\"com.apple.security.app-sandbox\"] exists"),
491 kSecCSDefaultFlags, &sandboxRequirement) != errSecSuccess)
494 if (SecStaticCodeCheckValidityWithErrors(staticCode,
495 kSecCSBasicValidateOnly, sandboxRequirement,
nullptr) != errSecSuccess)
503 std::scoped_lock lock(m_mutex);
504 if (m_thread.joinable())
507 bool isSandboxed()
const {
508 std::scoped_lock lock(m_mutex);
509 if (m_thread.joinable())
511 return m_isSandboxed;
515 mutable std::thread m_thread;
516 mutable std::mutex m_mutex;
519static SandboxChecker sandboxChecker;
524#if defined(Q_OS_MACOS)
525 return sandboxChecker.isSandboxed();
532@implementation NSObject (QtExtras)
533- (id)qt_valueForPrivateKey:(NSString *)key
535 if (qt_apple_isSandboxed())
538 return [self valueForKey:key];
546
547
548
549
550
551
554#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE
555@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end
556@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end
557QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER);
560const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] =
"QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
562QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
564 if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
569 [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
571 if (qstrcmp(qgetenv(
"OBJC_DEBUG_MISSING_POOLS"),
"YES") == 0) {
572 qDebug(
"QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n"
573 "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s",
574 __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH);
578QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
585#ifndef QT_BOOTSTRAPPED
586void qt_apple_check_os_version()
588#if defined(__WATCH_OS_VERSION_MIN_REQUIRED)
589 const char *os =
"watchOS";
590 const int version = __WATCH_OS_VERSION_MIN_REQUIRED;
591#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
592 const char *os =
"tvOS";
593 const int version = __TV_OS_VERSION_MIN_REQUIRED;
594#elif defined(__VISION_OS_VERSION_MIN_REQUIRED)
595 const char *os =
"visionOS";
596 const int version = __VISION_OS_VERSION_MIN_REQUIRED;
597#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
598 const char *os =
"iOS";
599 const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
600#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
601 const char *os =
"macOS";
602 const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
605 const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
606 const auto current = QOperatingSystemVersion::current().version();
608#if defined(Q_OS_MACOS)
611 if (current.majorVersion() == 10 && current.minorVersion() >= 16)
615 if (current < required) {
616 NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
617 NSString *applicationName = plist[@
"CFBundleDisplayName"];
618 if (!applicationName)
619 applicationName = plist[@
"CFBundleName"];
620 if (!applicationName)
621 applicationName = NSProcessInfo.processInfo.processName;
623 fprintf(stderr,
"Sorry, \"%s\" cannot be run on this version of %s. "
624 "Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n",
625 applicationName.UTF8String, os,
626 os,
long(required.majorVersion()),
long(required.minorVersion()),
long(required.microVersion()),
627 os,
long(current.majorVersion()),
long(current.minorVersion()),
long(current.microVersion()));
637void QMacNotificationObserver::remove()
640 [[NSNotificationCenter defaultCenter] removeObserver:observer];
646QMacKeyValueObserver::QMacKeyValueObserver(
const QMacKeyValueObserver &other)
647 : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get())
651void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
653 [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
656void QMacKeyValueObserver::removeObserver() {
658 [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
662KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
665@implementation QT_MANGLE_NAMESPACE(KeyValueObserver)
666- (
void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
667 change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(
void *)context
673 (*
reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
680#ifndef QT_BOOTSTRAPPED
681QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
684 case ApplicationBinary:
return applicationVersion().second;
685 case QtLibraries:
return libraryVersion().second;
690QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
693 case ApplicationBinary:
return applicationVersion().first;
694 case QtLibraries:
return libraryVersion().first;
699QOperatingSystemVersion QMacVersion::currentRuntime()
701 return QOperatingSystemVersion::current();
717QMacVersion::VersionTuple QMacVersion::versionsForImage(
const mach_header *machHeader)
719 static auto osForLoadCommand = [](uint32_t cmd) {
721 case LC_VERSION_MIN_MACOSX:
return QOperatingSystemVersion::MacOS;
722 case LC_VERSION_MIN_IPHONEOS:
return QOperatingSystemVersion::IOS;
723 case LC_VERSION_MIN_TVOS:
return QOperatingSystemVersion::TvOS;
724 case LC_VERSION_MIN_WATCHOS:
return QOperatingSystemVersion::WatchOS;
725 default:
return QOperatingSystemVersion::Unknown;
729 static auto osForPlatform = [](uint32_t platform) {
731 case Platform::macOS:
732 return QOperatingSystemVersion::MacOS;
734 case Platform::iOSSimulator:
735 return QOperatingSystemVersion::IOS;
737 case Platform::tvOSSimulator:
738 return QOperatingSystemVersion::TvOS;
739 case Platform::watchOS:
740 case Platform::watchOSSimulator:
741 return QOperatingSystemVersion::WatchOS;
743 return QOperatingSystemVersion::Unknown;
747 static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
749 QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
750 QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
754 const bool is64Bit = machHeader->magic == MH_MAGIC_64 || machHeader->magic == MH_CIGAM_64;
755 auto commandCursor = uintptr_t(machHeader) + (is64Bit ?
sizeof(mach_header_64) :
sizeof(mach_header));
757 for (uint32_t i = 0; i < machHeader->ncmds; ++i) {
758 load_command *loadCommand =
reinterpret_cast<load_command *>(commandCursor);
759 if (loadCommand->cmd == LC_VERSION_MIN_MACOSX || loadCommand->cmd == LC_VERSION_MIN_IPHONEOS
760 || loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
761 auto versionCommand =
reinterpret_cast<version_min_command *>(loadCommand);
762 return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
763 }
else if (loadCommand->cmd == LC_BUILD_VERSION) {
764 auto versionCommand =
reinterpret_cast<build_version_command *>(loadCommand);
765 return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
767 commandCursor += loadCommand->cmdsize;
769 Q_ASSERT_X(
false,
"QMacVersion",
"Could not find any version load command");
773QMacVersion::VersionTuple QMacVersion::applicationVersion()
775 static VersionTuple version = []() {
776 const mach_header *executableHeader =
nullptr;
777 for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
778 auto header = _dyld_get_image_header(i);
779 if (header->filetype == MH_EXECUTE) {
780 executableHeader = header;
784 Q_ASSERT_X(executableHeader,
"QMacVersion",
"Failed to resolve Mach-O header of executable");
785 return versionsForImage(executableHeader);
790QMacVersion::VersionTuple QMacVersion::libraryVersion()
792 static VersionTuple version = []() {
794 dladdr((
const void *)&QMacVersion::libraryVersion, &qtCoreImage);
795 Q_ASSERT_X(qtCoreImage.dli_fbase,
"QMacVersion",
"Failed to resolve Mach-O header of QtCore");
796 return versionsForImage(
static_cast<mach_header*>(qtCoreImage.dli_fbase));
804QObjCWeakPointerBase::QObjCWeakPointerBase(NSObject *object)
805 : m_weakReference(object)
809QObjCWeakPointerBase::QObjCWeakPointerBase(
const QObjCWeakPointerBase &other)
811 QMacAutoReleasePool pool;
812 m_weakReference = other.m_weakReference;
815QObjCWeakPointerBase &QObjCWeakPointerBase::operator=(
const QObjCWeakPointerBase &other)
817 QMacAutoReleasePool pool;
818 m_weakReference = other.m_weakReference;
822QObjCWeakPointerBase::~QObjCWeakPointerBase()
824 QMacAutoReleasePool pool;
825 m_weakReference = nil;
828NSObject *QObjCWeakPointerBase::get()
const
835 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)