diff options
author | Jason Molenda <jmolenda@apple.com> | 2019-08-07 02:06:06 +0000 |
---|---|---|
committer | Jason Molenda <jmolenda@apple.com> | 2019-08-07 02:06:06 +0000 |
commit | 6cebeafac31ceb500494bb301e365363c3d5992a (patch) | |
tree | 16a638ce3fb606f94e88888cfb760d92adea94ba /lldb/tools/debugserver | |
parent | 5dbfca85419bda9d432ec43f7ea54d64ee2aab55 (diff) | |
download | bcm5719-llvm-6cebeafac31ceb500494bb301e365363c3d5992a.tar.gz bcm5719-llvm-6cebeafac31ceb500494bb301e365363c3d5992a.zip |
Upstream a few small Apple changes to debugserver - arm64_32, Catalyst
Adrian's changes to support Catalyst processes and my
changes to support debugserver running on an arm64_32
device (Apple Watch Series 4, which uses an IPL32 model
on arm64 cpus).
llvm-svn: 368118
Diffstat (limited to 'lldb/tools/debugserver')
-rw-r--r-- | lldb/tools/debugserver/source/DNB.cpp | 6 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/DNB.h | 9 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/DNBDefs.h | 1 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachException.cpp | 2 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachProcess.h | 7 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachProcess.mm | 115 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp | 9 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp | 8 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/RNBRemote.cpp | 42 |
9 files changed, 170 insertions, 29 deletions
diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp index 32a73483379..5ac696774ba 100644 --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -1695,6 +1695,10 @@ bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) { return MachProcess::GetOSVersionNumbers(major, minor, patch); } +std::string DNBGetMacCatalystVersionString() { + return MachProcess::GetMacCatalystVersionString(); +} + void DNBInitialize() { DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()"); #if defined(__i386__) || defined(__x86_64__) @@ -1715,6 +1719,8 @@ nub_bool_t DNBSetArchitecture(const char *arch) { else if ((strcasecmp(arch, "x86_64") == 0) || (strcasecmp(arch, "x86_64h") == 0)) return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); + else if (strstr(arch, "arm64_32") == arch) + return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32); else if (strstr(arch, "arm64") == arch || strstr(arch, "armv8") == arch || strstr(arch, "aarch64") == arch) return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h index 074d0aa53f4..e29fa0fa636 100644 --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -20,6 +20,8 @@ #include "MacOSX/ThreadInfo.h" #include <mach/thread_info.h> #include <string> +#include <Availability.h> +#include <mach/machine.h> #define DNB_EXPORT __attribute__((visibility("default"))) @@ -27,6 +29,10 @@ #define CPU_TYPE_ARM64 ((cpu_type_t)12 | 0x01000000) #endif +#ifndef CPU_TYPE_ARM64_32 +#define CPU_TYPE_ARM64_32 ((cpu_type_t)12 | 0x02000000) +#endif + typedef bool (*DNBShouldCancelCallback)(void *); void DNBInitialize(); @@ -226,5 +232,6 @@ const char *DNBStateAsString(nub_state_t state); nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path, size_t resolved_path_size); bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch); - +/// \return the iOSSupportVersion of the host OS. +std::string DNBGetMacCatalystVersionString(); #endif diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h index cd61b31d619..22cfce1757f 100644 --- a/lldb/tools/debugserver/source/DNBDefs.h +++ b/lldb/tools/debugserver/source/DNBDefs.h @@ -341,6 +341,7 @@ enum DNBProfileDataScanType { (1 << 8), // Assume eProfileMemory, get Anonymous memory as well. eProfileEnergy = (1 << 9), + eProfileEnergyCPUCap = (1 << 10), eProfileMemoryCap = (1 << 15), diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.cpp b/lldb/tools/debugserver/source/MacOSX/MachException.cpp index 5023eb51d94..8690960e4cd 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachException.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachException.cpp @@ -489,8 +489,10 @@ const char *MachException::Name(exception_type_t exc_type) { return "EXC_MACH_SYSCALL"; case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; +#ifdef EXC_CRASH case EXC_CRASH: return "EXC_CRASH"; +#endif case EXC_RESOURCE: return "EXC_RESOURCE"; #ifdef EXC_GUARD diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h index 0b8e1f4d4a5..e31486cc39b 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -116,6 +116,7 @@ public: #endif static bool GetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch); + static std::string GetMacCatalystVersionString(); #ifdef WITH_BKS static void BKSCleanupAfterAttach(const void *attach_token, DNBError &err_str); @@ -233,12 +234,13 @@ public: GetDeploymentInfo(const struct load_command&, uint64_t load_command_address, uint32_t& major_version, uint32_t& minor_version, uint32_t& patch_version); - bool GetMachOInformationFromMemory(nub_addr_t mach_o_header_addr, + bool GetMachOInformationFromMemory(uint32_t platform, + nub_addr_t mach_o_header_addr, int wordsize, struct mach_o_information &inf); JSONGenerator::ObjectSP FormatDynamicLibrariesIntoJSON( const std::vector<struct binary_image_information> &image_infos); - void GetAllLoadedBinariesViaDYLDSPI( + uint32_t GetAllLoadedBinariesViaDYLDSPI( std::vector<struct binary_image_information> &image_infos); JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos( nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); @@ -425,6 +427,7 @@ private: const uuid_t uuid, const char *path)); void (*m_dyld_process_info_release)(void *info); void (*m_dyld_process_info_get_cache)(void *info, void *cacheInfo); + uint32_t (*m_dyld_process_info_get_platform)(void *info); }; #endif // __MachProcess_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm index 15a347778cf..a53ac9dda92 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -44,6 +44,30 @@ #include "CFBundle.h" #include "CFString.h" +#ifndef PLATFORM_BRIDGEOS +#define PLATFORM_BRIDGEOS 5 +#endif + +#ifndef PLATFORM_MACCATALYST +#define PLATFORM_MACCATALYST 6 +#endif + +#ifndef PLATFORM_IOSSIMULATOR +#define PLATFORM_IOSSIMULATOR 7 +#endif + +#ifndef PLATFORM_TVOSSIMULATOR +#define PLATFORM_TVOSSIMULATOR 8 +#endif + +#ifndef PLATFORM_WATCHOSSIMULATOR +#define PLATFORM_WATCHOSSIMULATOR 9 +#endif + +#ifndef PLATFORM_DRIVERKIT +#define PLATFORM_DRIVERKIT 10 +#endif + #ifdef WITH_SPRINGBOARD #include <CoreFoundation/CoreFoundation.h> @@ -480,6 +504,8 @@ MachProcess::MachProcess() (void (*)(void *info))dlsym(RTLD_DEFAULT, "_dyld_process_info_release"); m_dyld_process_info_get_cache = (void (*)(void *info, void *cacheInfo))dlsym( RTLD_DEFAULT, "_dyld_process_info_get_cache"); + m_dyld_process_info_get_platform = (uint32_t (*)(void *info))dlsym( + RTLD_DEFAULT, "_dyld_process_info_get_platform"); DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); } @@ -600,11 +626,6 @@ const char *MachProcess::GetDeploymentInfo(const struct load_command& lc, } } #if defined (LC_BUILD_VERSION) -#ifndef PLATFORM_IOSSIMULATOR -#define PLATFORM_IOSSIMULATOR 7 -#define PLATFORM_TVOSSIMULATOR 8 -#define PLATFORM_WATCHOSSIMULATOR 9 -#endif if (cmd == LC_BUILD_VERSION) { struct build_version_command build_vers; if (ReadMemory(load_command_address, sizeof(struct build_version_command), @@ -618,6 +639,8 @@ const char *MachProcess::GetDeploymentInfo(const struct load_command& lc, switch (build_vers.platform) { case PLATFORM_MACOS: return "macosx"; + case PLATFORM_MACCATALYST: + return "maccatalyst"; case PLATFORM_IOS: case PLATFORM_IOSSIMULATOR: return "ios"; @@ -629,6 +652,8 @@ const char *MachProcess::GetDeploymentInfo(const struct load_command& lc, return "watchos"; case PLATFORM_BRIDGEOS: return "bridgeos"; + case PLATFORM_DRIVERKIT: + return "driverkit"; } } #endif @@ -643,7 +668,7 @@ const char *MachProcess::GetDeploymentInfo(const struct load_command& lc, // commands. bool MachProcess::GetMachOInformationFromMemory( - nub_addr_t mach_o_header_addr, int wordsize, + uint32_t dyld_platform, nub_addr_t mach_o_header_addr, int wordsize, struct mach_o_information &inf) { uint64_t load_cmds_p; if (wordsize == 4) { @@ -735,17 +760,42 @@ bool MachProcess::GetMachOInformationFromMemory( } uint32_t major_version, minor_version, patch_version; - if (const char *platform = GetDeploymentInfo(lc, load_cmds_p, - major_version, minor_version, - patch_version)) { - inf.min_version_os_name = platform; - inf.min_version_os_version = ""; - inf.min_version_os_version += std::to_string(major_version); - inf.min_version_os_version += "."; - inf.min_version_os_version += std::to_string(minor_version); - if (patch_version != 0) { + if (const char *lc_platform = GetDeploymentInfo( + lc, load_cmds_p, major_version, minor_version, patch_version)) { + // APPLE INTERNAL: macCatalyst support + // This handles two special cases: + // 1. Zippered frameworks have two deployment info load commands. + // Make sure to select the requested one. + // 2. The xctest binary is a pure macOS binary but is launched with + // DYLD_FORCE_PLATFORM=6. + if (dyld_platform == PLATFORM_MACCATALYST && + inf.mach_header.filetype == MH_EXECUTE && + inf.min_version_os_name.empty() && + (strcmp("macosx", lc_platform) == 0)) { + // DYLD says this *is* a macCatalyst process. If we haven't + // parsed any load commands, transform a macOS load command + // into a generic macCatalyst load command. It will be + // overwritten by a more specific one if there is one. This + // is only done for the main executable. It is perfectly fine + // for a macCatalyst binary to link against a macOS-only framework. + inf.min_version_os_name = "maccatalyst"; + inf.min_version_os_version = GetMacCatalystVersionString(); + } else if (dyld_platform != PLATFORM_MACCATALYST && + inf.min_version_os_name == "macosx") { + // This is a zippered binary and the process is not running as + // PLATFORM_MACCATALYST. Stick with the macosx load command + // that we've already processed, ignore this one, which is + // presumed to be a PLATFORM_MACCATALYST one. + } else { + inf.min_version_os_name = lc_platform; + inf.min_version_os_version = ""; + inf.min_version_os_version += std::to_string(major_version); inf.min_version_os_version += "."; - inf.min_version_os_version += std::to_string(patch_version); + inf.min_version_os_version += std::to_string(minor_version); + if (patch_version != 0) { + inf.min_version_os_version += "."; + inf.min_version_os_version += std::to_string(patch_version); + } } } @@ -941,7 +991,10 @@ JSONGenerator::ObjectSP MachProcess::GetLoadedDynamicLibrariesInfos( //// Second, read the mach header / load commands for all the dylibs for (size_t i = 0; i < image_count; i++) { - if (!GetMachOInformationFromMemory(image_infos[i].load_address, + // The SPI to provide platform is not available on older systems. + uint32_t platform = 0; + if (!GetMachOInformationFromMemory(platform, + image_infos[i].load_address, pointer_size, image_infos[i].macho_info)) { return reply_sp; @@ -972,8 +1025,9 @@ struct dyld_process_cache_info { // binary_image_information' - call // GetMachOInformationFromMemory to fill in the mach-o header/load command // details. -void MachProcess::GetAllLoadedBinariesViaDYLDSPI( +uint32_t MachProcess::GetAllLoadedBinariesViaDYLDSPI( std::vector<struct binary_image_information> &image_infos) { + uint32_t platform = 0; kern_return_t kern_ret; if (m_dyld_process_info_create) { dyld_process_info info = @@ -988,9 +1042,12 @@ void MachProcess::GetAllLoadedBinariesViaDYLDSPI( image.load_address = mach_header_addr; image_infos.push_back(image); }); + if (m_dyld_process_info_get_platform) + platform = m_dyld_process_info_get_platform(info); m_dyld_process_info_release(info); } } + return platform; } // Fetch information about all shared libraries using the dyld SPIs that exist @@ -1011,10 +1068,11 @@ MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid) { pointer_size = 8; std::vector<struct binary_image_information> image_infos; - GetAllLoadedBinariesViaDYLDSPI(image_infos); + uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(image_infos); const size_t image_count = image_infos.size(); for (size_t i = 0; i < image_count; i++) { - GetMachOInformationFromMemory(image_infos[i].load_address, pointer_size, + GetMachOInformationFromMemory(platform, + image_infos[i].load_address, pointer_size, image_infos[i].macho_info); } return FormatDynamicLibrariesIntoJSON(image_infos); @@ -1040,7 +1098,7 @@ JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses( pointer_size = 8; std::vector<struct binary_image_information> all_image_infos; - GetAllLoadedBinariesViaDYLDSPI(all_image_infos); + uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(all_image_infos); std::vector<struct binary_image_information> image_infos; const size_t macho_addresses_count = macho_addresses.size(); @@ -1055,7 +1113,8 @@ JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses( const size_t image_infos_count = image_infos.size(); for (size_t i = 0; i < image_infos_count; i++) { - GetMachOInformationFromMemory(image_infos[i].load_address, pointer_size, + GetMachOInformationFromMemory(platform, + image_infos[i].load_address, pointer_size, image_infos[i].macho_info); } return FormatDynamicLibrariesIntoJSON(image_infos); @@ -2543,6 +2602,18 @@ bool MachProcess::GetOSVersionNumbers(uint64_t *major, uint64_t *minor, #endif } +std::string MachProcess::GetMacCatalystVersionString() { + @autoreleasepool { + NSDictionary *version_info = + [NSDictionary dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *version_value = [version_info objectForKey: @"iOSSupportVersion"]; + if (const char *version_str = [version_value UTF8String]) + return version_str; + } + return {}; +} + // Do the process specific setup for attach. If this returns NULL, then there's // no // platform specific stuff to be done to wait for the attach. If you get diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp index 0fa4437843a..d2aae9da0c4 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp @@ -12,6 +12,7 @@ #include "MachThreadList.h" +#include "DNB.h" #include "DNBLog.h" #include "DNBThreadResumeActions.h" #include "MachProcess.h" @@ -278,8 +279,12 @@ MachThreadList::UpdateThreadList(MachProcess *process, bool update, #elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) if (m_is_64_bit) DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); - else - DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM); + else { + if (process->GetCPUType() == CPU_TYPE_ARM64_32) + DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32); + else + DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM); + } #endif } diff --git a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp index ca4b46ac65c..8856e921997 100644 --- a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp +++ b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp @@ -69,6 +69,14 @@ void DNBArchMachARM64::Initialize() { // Register this arch plug-in with the main protocol class DNBArchProtocol::RegisterArchPlugin(arch_plugin_info); + + DNBArchPluginInfo arch_plugin_info_32 = { + CPU_TYPE_ARM64_32, DNBArchMachARM64::Create, + DNBArchMachARM64::GetRegisterSetInfo, + DNBArchMachARM64::SoftwareBreakpointOpcode}; + + // Register this arch plug-in with the main protocol class + DNBArchProtocol::RegisterArchPlugin(arch_plugin_info_32); } DNBArchProtocol *DNBArchMachARM64::Create(MachThread *thread) { diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index 3a4035b0b9b..ad7e65b351a 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -1287,6 +1287,9 @@ static cpu_type_t best_guess_cpu_type() { if (sizeof(char *) == 8) { return CPU_TYPE_ARM64; } else { +#if defined (__ARM64_ARCH_8_32__) + return CPU_TYPE_ARM64_32; +#endif return CPU_TYPE_ARM; } #elif defined(__i386__) || defined(__x86_64__) @@ -4557,6 +4560,8 @@ static const char *GetArchName(const uint32_t cputype, break; case CPU_TYPE_ARM64: return "arm64"; + case CPU_TYPE_ARM64_32: + return "arm64_32"; case CPU_TYPE_I386: return "i386"; case CPU_TYPE_X86_64: @@ -4590,6 +4595,10 @@ static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype, g_host_cputype |= CPU_ARCH_ABI64; } } +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + if (g_host_cputype == CPU_TYPE_ARM64 && sizeof (void*) == 4) + g_host_cputype = CPU_TYPE_ARM64_32; +#endif } len = sizeof(uint32_t); @@ -4599,6 +4608,16 @@ static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype, g_host_cpusubtype == CPU_SUBTYPE_486) g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL; } +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + // on arm64_32 devices, the machine's native cpu type is + // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e. + // But we change the cputype to CPU_TYPE_ARM64_32 because + // the user processes are all ILP32 processes today. + // We also need to rewrite the cpusubtype so we vend + // a valid cputype + cpusubtype combination. + if (g_host_cputype == CPU_TYPE_ARM64_32) + g_host_cpusubtype = CPU_SUBTYPE_ARM64_32_V8; +#endif } cputype = g_host_cputype; @@ -4623,7 +4642,8 @@ rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) { // The OS in the triple should be "ios" or "macosx" which doesn't match our // "Darwin" which gets returned from "kern.ostype", so we need to hardcode // this for now. - if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) { + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64 + || cputype == CPU_TYPE_ARM64_32) { #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 strm << "ostype:tvos;"; #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 @@ -4660,6 +4680,12 @@ rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) { strm << ";"; } + std::string maccatalyst_version = DNBGetMacCatalystVersionString(); + if (!maccatalyst_version.empty() && + std::all_of(maccatalyst_version.begin(), maccatalyst_version.end(), + [](char c) { return (c >= '0' && c <= '9') || c == '.'; })) + strm << "maccatalyst_version:" << maccatalyst_version << ";"; + #if defined(__LITTLE_ENDIAN__) strm << "endian:little;"; #elif defined(__BIG_ENDIAN__) @@ -6034,6 +6060,17 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) { cpusubtype = 12; // CPU_SUBTYPE_ARM_V7K } } +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + // on arm64_32 devices, the machine's native cpu type is + // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e. + // But we change the cputype to CPU_TYPE_ARM64_32 because + // the user processes are all ILP32 processes today. + // We also need to rewrite the cpusubtype so we vend + // a valid cputype + cpusubtype combination. + if (cputype == CPU_TYPE_ARM64_32 && cpusubtype == 2) + cpusubtype = CPU_SUBTYPE_ARM64_32_V8; +#endif + rep << "cpusubtype:" << std::hex << cpusubtype << ';'; } @@ -6080,7 +6117,8 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) { // The OS in the triple should be "ios" or "macosx" which doesn't match our // "Darwin" which gets returned from "kern.ostype", so we need to hardcode // this for now. - if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) { + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64 + || cputype == CPU_TYPE_ARM64_32) { #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 rep << "ostype:tvos;"; #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |