Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcore_mac.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2014 Petroules Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <private/qcore_mac_p.h>
6
7#ifdef Q_OS_MACOS
8#include <AppKit/AppKit.h>
9#endif
10
11#if defined(QT_PLATFORM_UIKIT)
12#include <UIKit/UIKit.h>
13#endif
14
15#include <new>
16#include <execinfo.h>
17#include <dlfcn.h>
18#include <cxxabi.h>
19#include <objc/runtime.h>
20#include <mach-o/dyld.h>
21#include <sys/sysctl.h>
22#include <spawn.h>
23
24#include <qdebug.h>
25
26#include "qendian.h"
27#include "qhash.h"
28#include "qmutex.h"
30#include "private/qlocking_p.h"
31
32#if !defined(QT_BOOTSTRAPPED)
33#include <thread>
34#endif
35
36#if !defined(QT_APPLE_NO_PRIVATE_APIS)
37extern "C" {
38typedef uint32_t csr_config_t;
39extern int csr_get_active_config(csr_config_t *) __attribute__((weak_import));
40
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));
45char *** _NSGetArgv();
46extern char **environ;
47#endif
48}
49#endif
50
51QT_BEGIN_NAMESPACE
52
53using namespace Qt::StringLiterals;
54
55// --------------------------------------------------------------------------
56
57#if defined(Q_OS_MACOS)
58static void initializeStandardUserDefaults()
59{
60 // The standard user defaults are initialized from an ordered list of domains,
61 // as documented by NSUserDefaults.standardUserDefaults. This includes e.g.
62 // parsing command line arguments, such as -AppleFooBar "baz", as well as
63 // global defaults. To ensure that these defaults are available through
64 // the lower level Core Foundation preferences APIs, we need to initialize
65 // them as early as possible via the Foundation-API, as the lower level APIs
66 // do not do this initialization.
67 Q_UNUSED(NSUserDefaults.standardUserDefaults);
68}
69Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
70#endif
71
72// --------------------------------------------------------------------------
73
74QCFString::operator QString() const
75{
76 if (string.isEmpty() && value)
77 const_cast<QCFString*>(this)->string = QString::fromCFString(value);
78 return string;
79}
80
81QCFString::operator CFStringRef() const
82{
83 if (!value)
84 const_cast<QCFString*>(this)->value = string.toCFString();
85 return value;
86}
87
88// --------------------------------------------------------------------------
89
90#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
91
92bool AppleUnifiedLogger::preventsStderrLogging()
93{
94 // os_log will mirror to stderr if OS_ACTIVITY_DT_MODE is set,
95 // regardless of its value. OS_ACTIVITY_MODE then controls whether
96 // to include info and/or debug messages in this mirroring.
97 // For some reason, when launched under lldb (via Xcode or not),
98 // all levels are included.
99
100 // CFLog will normally log to both stderr, and via os_log.
101 // Setting CFLOG_FORCE_DISABLE_STDERR disables the stderr
102 // logging. Setting CFLOG_FORCE_STDERR will both duplicate
103 // CFLog's output to stderr, and trigger OS_ACTIVITY_DT_MODE,
104 // resulting in os_log calls also being mirrored to stderr.
105 // Setting ACTIVITY_LOG_STDERR has the same effect.
106
107 // NSLog is plumbed to CFLog, and will respond to the same
108 // environment variables as CFLog.
109
110 // We want to disable Qt's default stderr log handler when
111 // os_log has already mirrored to stderr.
112 static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
113 || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
114 || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
115
116 // As well as when we suspect that Xcode is going to present os_log
117 // as structured log messages.
118 static bool disableStderr = qEnvironmentVariableIsSet("CFLOG_FORCE_DISABLE_STDERR");
119
120 return willMirror || disableStderr;
121}
122
123QT_MAC_WEAK_IMPORT(_os_log_default);
124bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context,
125 const QString &message, const QString &optionalSubsystem)
126{
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);
133 }
134 return QString();
135 }();
136 subsystem = bundleIdentifier;
137 }
138
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);
143
144 if (!os_log_type_enabled(log, logType))
145 return false;
146
147 // Logging best practices says we should not include symbolication
148 // information or source file line numbers in messages, as the system
149 // will automatically captures this information. In our case, what
150 // the system captures is the call to os_log_with_type below, which
151 // isn't really useful, but we still don't want to include the context's
152 // info, as that would clutter the logging output. See rdar://35958308.
153
154 // The format must be a string constant, so we can't pass on the
155 // message. This means we won't be able to take advantage of the
156 // unified logging's custom format specifiers such as %{BOOL}d.
157 // We use the 'public' format specifier to prevent the logging
158 // system from redacting our log message.
159 os_log_with_type(log, logType, "%{public}s", qPrintable(message));
160
161 return preventsStderrLogging();
162}
163
164os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
165{
166 switch (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;
172 }
173
174 return OS_LOG_TYPE_DEFAULT;
175}
176
177#endif // QT_USE_APPLE_UNIFIED_LOGGING
178
179#ifndef QT_NO_DEBUG_STREAM
180// -------------------------------------------------------------------------
181
182QDebug operator<<(QDebug dbg, id obj)
183{
184 if (!obj) {
185 // Match NSLog
186 dbg << "(null)";
187 return dbg;
188 }
189
190 for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
191 if (cls == NSObject.class) {
192 dbg << static_cast<NSObject*>(obj);
193 return dbg;
194 }
195 }
196
197 // Match NSObject.debugDescription
198 const QDebugStateSaver saver(dbg);
199 dbg.nospace() << '<' << object_getClassName(obj) << ": " << static_cast<void*>(obj) << '>';
200 return dbg;
201}
202
203QDebug operator<<(QDebug dbg, const NSObject *nsObject)
204{
205 return dbg << (nsObject ?
206 dbg.verbosity() > 2 ?
207 nsObject.debugDescription.UTF8String :
208 nsObject.description.UTF8String
209 : "NSObject(0x0)");
210}
211
212QDebug operator<<(QDebug dbg, CFStringRef stringRef)
213{
214 if (!stringRef)
215 return dbg << "CFStringRef(0x0)";
216
217 if (const UniChar *chars = CFStringGetCharactersPtr(stringRef))
218 dbg << QStringView(reinterpret_cast<const QChar *>(chars), CFStringGetLength(stringRef));
219 else
220 dbg << QString::fromCFString(stringRef);
221
222 return dbg;
223}
224
225// Prevents breaking the ODR in case we introduce support for more types
226// later on, and lets the user override our default QDebug operators.
227#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
228 __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
229
234#endif // QT_NO_DEBUG_STREAM
235
236// -------------------------------------------------------------------------
237
238QT_END_NAMESPACE
239QT_USE_NAMESPACE
240
241#ifdef QT_DEBUG
242@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
243@end
244
245@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
246@end
247QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
248#endif // QT_DEBUG
249
250// Use the direct runtime interface to manage autorelease pools, as it
251// has less overhead then allocating NSAutoreleasePools, and allows for
252// a future where we use ARC (where NSAutoreleasePool is not allowed).
253// https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support
254
255extern "C" {
257void objc_autoreleasePoolPop(void *pool);
258}
259
260QT_BEGIN_NAMESPACE
261
262QMacAutoReleasePool::QMacAutoReleasePool()
263 : pool(objc_autoreleasePoolPush())
264{
265#ifdef QT_DEBUG
266 static const bool debugAutoReleasePools = qEnvironmentVariableIsSet("QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
267 if (!debugAutoReleasePools)
268 return;
269
270 Class trackerClass = [QMacAutoReleasePoolTracker class];
271
272 void *poolFrame = nullptr;
273 void *frames[2];
274 if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
275 poolFrame = frames[1];
276
277 if (poolFrame) {
278 Dl_info info;
279 if (dladdr(poolFrame, &info) && info.dli_sname) {
280 const char *symbolName = info.dli_sname;
281 if (symbolName[0] == '_') {
282 int status;
283 if (char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status))
284 symbolName = demangled;
285 }
286
287 char *className = nullptr;
288 asprintf(&className, " ^-- allocated in function: %s", symbolName);
289
290 if (Class existingClass = objc_getClass(className))
291 trackerClass = existingClass;
292 else
293 trackerClass = objc_duplicateClass(trackerClass, className, 0);
294
295 free(className);
296
297 if (symbolName != info.dli_sname)
298 free((char*)symbolName);
299 }
300 }
301
302 [[trackerClass new] autorelease];
303#endif // QT_DEBUG
304}
305
306QMacAutoReleasePool::~QMacAutoReleasePool()
307{
308 objc_autoreleasePoolPop(pool);
309}
310
311#ifndef QT_NO_DEBUG_STREAM
312QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool)
313{
314 QDebugStateSaver saver(debug);
315 debug.nospace();
316 debug << "QMacAutoReleasePool(" << (const void *)pool << ')';
317 return debug;
318}
319
320QDebug operator<<(QDebug debug, const QCFString &string)
321{
322 debug << static_cast<QString>(string);
323 return debug;
324}
325#endif // !QT_NO_DEBUG_STREAM
326
327#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
328bool qt_mac_runningUnderRosetta()
329{
330 int translated = 0;
331 auto size = sizeof(translated);
332 if (sysctlbyname("sysctl.proc_translated", &translated, &size, nullptr, 0) == 0)
333 return translated;
334 return false;
335}
336
337bool qt_apple_runningWithLiquidGlass()
338{
339 static const bool runningWithLiquidGlass = []{
340 if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary).majorVersion() < 26)
341 return false;
342
343 if (QMacVersion::currentRuntime().majorVersion() < 26)
344 return false;
345
346 // Word on the street is that the opt out will only work for
347 // macOS 26, but it's not clear whether building against the
348 // Xcode 27 SDK is what will disable it, or simply running on
349 // macOS 27. Let's go with the latter for now.
350 if (QMacVersion::currentRuntime().majorVersion() < 27) {
351 const id liquidGlassOptOut = [NSBundle.mainBundle
352 objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"];
353 if (liquidGlassOptOut && [liquidGlassOptOut boolValue])
354 return false;
355 }
356
357 return true;
358 }();
359
360 return runningWithLiquidGlass;
361}
362
363std::optional<uint32_t> qt_mac_sipConfiguration()
364{
365 static auto configuration = []() -> std::optional<uint32_t> {
366#if !defined(QT_APPLE_NO_PRIVATE_APIS)
367 csr_config_t config;
368 if (csr_get_active_config && csr_get_active_config(&config) == 0)
369 return config;
370#endif
371
372 QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
373 if (!nvram) {
374 qWarning("Failed to locate NVRAM entry in IO registry");
375 return {};
376 }
377
378 QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
379 CFSTR("csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
380 if (!csrConfig)
381 return {}; // SIP config is not available
382
383 if (auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
384#ifndef QT_NO_DEBUG_STREAM
385 qWarning() << "Unexpected SIP config type" << CFCopyTypeIDDescription(type);
386#endif
387 return {};
388 }
389
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()));
393 return {};
394 }
395
396 return qFromLittleEndian<uint32_t>(data.constData());
397 }();
398 return configuration;
399}
400
401bool qt_mac_processHasEntitlement(const QString &entitlement)
402{
403 if (QCFType<SecTaskRef> task = SecTaskCreateFromSelf(kCFAllocatorDefault)) {
404 if (QCFType<CFTypeRef> value = SecTaskCopyValueForEntitlement(task,
405 entitlement.toCFString(), nullptr)) {
406
407 if (CFGetTypeID(value) != CFBooleanGetTypeID())
408 return false;
409
410 return CFBooleanGetValue(value.as<CFBooleanRef>());
411 }
412 }
413 return false;
414}
415
416#define CHECK_SPAWN(expr)
417 if ((expr) != 0) {
418 posix_spawnattr_destroy(&attr);
419 return;
420 }
421
422#ifdef QT_BUILD_INTERNAL
423void qt_mac_ensureResponsible()
424{
425#if !defined(QT_APPLE_NO_PRIVATE_APIS)
426 if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
427 return;
428
429 auto pid = getpid();
430 if (responsibility_get_pid_responsible_for_pid(pid) == pid)
431 return; // Already responsible
432
433 posix_spawnattr_t attr = {};
434 CHECK_SPAWN(posix_spawnattr_init(&attr));
435
436 // Behave as exec
437 short flags = POSIX_SPAWN_SETEXEC;
438
439 // Reset signal mask
440 sigset_t no_signals;
441 sigemptyset(&no_signals);
442 CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
443 flags |= POSIX_SPAWN_SETSIGMASK;
444
445 // Reset all signals to their default handlers
446 sigset_t all_signals;
447 sigfillset(&all_signals);
448 CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
449 flags |= POSIX_SPAWN_SETSIGDEF;
450
451 CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
452
453 CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
454
455 char **argv = *_NSGetArgv();
456 posix_spawnp(&pid, argv[0], nullptr, &attr, argv, environ);
457 posix_spawnattr_destroy(&attr);
458#endif
459}
460#endif // QT_BUILD_INTERNAL
461
462#endif
463
465{
466 static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSExtension"];
467 return isExtension;
468}
469
470#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
472{
473 // Application extensions are not allowed to access the shared application
475 qWarning() << "accessing the shared" << [AppleApplication class]
476 << "is not allowed in application extensions";
477
478 // In practice the application is actually available, but the App
479 // review process will likely catch uses of it, so we return nil just
480 // in case, unless we don't care about being App Store compliant.
481#if QT_CONFIG(appstore_compliant)
482 return nil;
483#endif
484 }
485
486 // We use performSelector so that building with -fapplication-extension will
487 // not mistakenly think we're using the shared application in extensions.
488 return [[AppleApplication class] performSelector:@selector(sharedApplication)];
489}
490#endif
491
492#if !defined(QT_BOOTSTRAPPED)
493
495{
496#if defined(Q_OS_MACOS)
497 static bool isSandboxed = qt_mac_processHasEntitlement(u"com.apple.security.app-sandbox"_s);
498 return isSandboxed;
499#else
500 return true; // All other Apple platforms
501#endif
502}
503
504QT_END_NAMESPACE
505@implementation NSObject (QtExtras)
506- (id)qt_valueForPrivateKey:(NSString *)key
507{
508 if (qt_apple_isSandboxed())
509 return nil;
510
511 return [self valueForKey:key];
512}
513@end
514QT_BEGIN_NAMESPACE
515#endif // !QT_BOOTSTRAPPED
516
517#ifdef Q_OS_MACOS
518/*
519 Ensure that Objective-C objects auto-released in main(), directly or indirectly,
520 after QCoreApplication construction, are released when the app goes out of scope.
521 The memory will be reclaimed by the system either way when the process exits,
522 but by having a root level pool we ensure that the objects get their dealloc
523 methods called, which is useful for debugging object ownership graphs, etc.
524*/
525
526QT_END_NAMESPACE
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);
531QT_BEGIN_NAMESPACE
532
533const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] = "QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
534
535QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
536{
537 if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
538 return;
539
540 pool.emplace();
541
542 [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
543
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);
548 }
549}
550
551QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
552{
553}
554#endif
555
556// -------------------------------------------------------------------------
557
558#ifndef QT_BOOTSTRAPPED
559void qt_apple_check_os_version()
560{
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;
576#endif
577
578 const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
579 const auto current = QOperatingSystemVersion::current().version();
580
581#if defined(Q_OS_MACOS)
582 // Check for compatibility version, in which case we can't do a
583 // comparison to the deployment target, which might be e.g. 11.0
584 if (current.majorVersion() == 10 && current.minorVersion() >= 16)
585 return;
586#endif
587
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;
595
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()));
601
602 exit(1);
603 }
604}
606#endif // QT_BOOTSTRAPPED
607
608// -------------------------------------------------------------------------
609
610void QMacNotificationObserver::remove()
611{
612 if (observer)
613 [[NSNotificationCenter defaultCenter] removeObserver:observer];
614 observer = nullptr;
615}
616
617// -------------------------------------------------------------------------
618
619QMacKeyValueObserver::QMacKeyValueObserver(const QMacKeyValueObserver &other)
620 : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get())
621{
622}
623
624void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
625{
626 [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
627}
628
629void QMacKeyValueObserver::removeObserver() {
630 if (object)
631 [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
632 object = nil;
633}
634
635KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
636
637QT_END_NAMESPACE
638@implementation QT_MANGLE_NAMESPACE(KeyValueObserver)
639- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
640 change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
641{
642 Q_UNUSED(keyPath);
643 Q_UNUSED(object);
644 Q_UNUSED(change);
645
646 (*reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
647}
648@end
649QT_BEGIN_NAMESPACE
650
651// -------------------------------------------------------------------------
652
653#ifndef QT_BOOTSTRAPPED
654QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
655{
656 switch (target) {
657 case ApplicationBinary: return applicationVersion().second;
658 case QtLibraries: return libraryVersion().second;
659 }
660 Q_UNREACHABLE();
661}
662
663QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
664{
665 switch (target) {
666 case ApplicationBinary: return applicationVersion().first;
667 case QtLibraries: return libraryVersion().first;
668 }
669 Q_UNREACHABLE();
670}
671
672QOperatingSystemVersion QMacVersion::currentRuntime()
673{
674 return QOperatingSystemVersion::current();
675}
676
677// Mach-O platforms
678enum Platform {
679 macOS = 1,
680 iOS = 2,
681 tvOS = 3,
682 watchOS = 4,
683 bridgeOS = 5,
684 macCatalyst = 6,
685 iOSSimulator = 7,
686 tvOSSimulator = 8,
687 watchOSSimulator = 9
688};
689
690QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machHeader)
691{
692 static auto osForLoadCommand = [](uint32_t cmd) {
693 switch (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;
699 }
700 };
701
702 static auto osForPlatform = [](uint32_t platform) {
703 switch (platform) {
704 case Platform::macOS:
705 return QOperatingSystemVersion::MacOS;
706 case Platform::iOS:
707 case Platform::iOSSimulator:
708 return QOperatingSystemVersion::IOS;
709 case Platform::tvOS:
710 case Platform::tvOSSimulator:
711 return QOperatingSystemVersion::TvOS;
712 case Platform::watchOS:
713 case Platform::watchOSSimulator:
714 return QOperatingSystemVersion::WatchOS;
715 default:
716 return QOperatingSystemVersion::Unknown;
717 }
718 };
719
720 static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
721 return std::pair(
722 QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
723 QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
724 );
725 };
726
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));
729
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));
739 }
740 commandCursor += loadCommand->cmdsize;
741 }
742 Q_ASSERT_X(false, "QMacVersion", "Could not find any version load command");
743 Q_UNREACHABLE();
744}
745
746QMacVersion::VersionTuple QMacVersion::applicationVersion()
747{
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;
754 break;
755 }
756 }
757 Q_ASSERT_X(executableHeader, "QMacVersion", "Failed to resolve Mach-O header of executable");
758 return versionsForImage(executableHeader);
759 }();
760 return version;
761}
762
763QMacVersion::VersionTuple QMacVersion::libraryVersion()
764{
765 static VersionTuple version = []() {
766 Dl_info qtCoreImage;
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));
770 }();
771 return version;
772}
773#endif // QT_BOOTSTRAPPED
774
775// -------------------------------------------------------------------------
776
777QObjCWeakPointerBase::QObjCWeakPointerBase(NSObject *object)
778 : m_weakReference(object)
779{
780}
781
782QObjCWeakPointerBase::QObjCWeakPointerBase(const QObjCWeakPointerBase &other)
783{
784 QMacAutoReleasePool pool;
785 m_weakReference = other.m_weakReference;
786}
787
788QObjCWeakPointerBase &QObjCWeakPointerBase::operator=(const QObjCWeakPointerBase &other)
789{
790 QMacAutoReleasePool pool;
791 m_weakReference = other.m_weakReference;
792 return *this;
793}
794
795QObjCWeakPointerBase::~QObjCWeakPointerBase()
796{
797 QMacAutoReleasePool pool;
798 m_weakReference = nil;
799}
800
801NSObject *QObjCWeakPointerBase::get() const
802{
803 // Loading from a __weak variable will retain and auto-release (in non-ARC).
804 // Unlike cases above, we want the object to stay alive until the outer
805 // auto-release pool is drained, so that consumers of QObjCWeakPointer
806 // can trust that the variable they get back will be alive, similar to
807 // the semantics of loading from __weak.
808 return m_weakReference;
809}
810
811// -------------------------------------------------------------------------
812
813QT_END_NAMESPACE
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))
uint32_t csr_config_t
Definition qcore_mac.mm:38
void objc_autoreleasePoolPop(void *pool)
QT_END_NAMESPACE QT_USE_NAMESPACE void * objc_autoreleasePoolPush(void)
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
Definition qcore_mac.mm:203
AppleApplication * qt_apple_sharedApplication()
Definition qcore_mac.mm:471
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
Q_CORE_EXPORT bool qt_apple_isApplicationExtension()
Definition qcore_mac.mm:464
Q_CORE_EXPORT bool qt_apple_isSandboxed()
Definition qcore_mac.mm:494
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)