Index: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp =================================================================== --- lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -681,11 +681,16 @@ loaded_module_list.AppendIfNeeded(image_module_sp); } - // macCatalyst support: - // Update the module's platform with the DYLD info. + // To support macCatalyst and legacy iOS simulator, + // update the module's platform with the DYLD info. ArchSpec dyld_spec = image_infos[idx].GetArchitecture(); - if (dyld_spec.GetTriple().getOS() == llvm::Triple::IOS && - dyld_spec.GetTriple().getEnvironment() == llvm::Triple::MacABI) + auto &dyld_triple = dyld_spec.GetTriple(); + if ((dyld_triple.getEnvironment() == llvm::Triple::MacABI && + dyld_triple.getOS() == llvm::Triple::IOS) || + (dyld_triple.getEnvironment() == llvm::Triple::Simulator && + (dyld_triple.getOS() == llvm::Triple::IOS || + dyld_triple.getOS() == llvm::Triple::TvOS || + dyld_triple.getOS() == llvm::Triple::WatchOS))) image_module_sp->MergeArchitecture(dyld_spec); } } @@ -748,13 +753,23 @@ // Update the module's platform with the DYLD info. lldb_private::ArchSpec arch_spec(lldb_private::eArchTypeMachO, header.cputype, header.cpusubtype); - if (os_type == llvm::Triple::IOS && os_env == llvm::Triple::MacABI) { - llvm::Triple triple(llvm::Twine("x86_64-apple-ios") + min_version_os_sdk + - "-macabi"); + if (os_env == llvm::Triple::MacABI && os_type == llvm::Triple::IOS) { + llvm::Triple triple(llvm::Twine(arch_spec.GetArchitectureName()) + + "-apple-ios" + min_version_os_sdk + "-macabi"); ArchSpec maccatalyst_spec(triple); if (arch_spec.IsCompatibleMatch(maccatalyst_spec)) arch_spec.MergeFrom(maccatalyst_spec); } + if (os_env == llvm::Triple::Simulator && + (os_type == llvm::Triple::IOS || os_type == llvm::Triple::TvOS || + os_type == llvm::Triple::WatchOS)) { + llvm::Triple triple(llvm::Twine(arch_spec.GetArchitectureName()) + + "-apple-" + llvm::Triple::getOSTypeName(os_type) + + min_version_os_sdk + "-simulator"); + ArchSpec sim_spec(triple); + if (arch_spec.IsCompatibleMatch(sim_spec)) + arch_spec.MergeFrom(sim_spec); + } return arch_spec; } Index: lldb/test/API/macosx/simulator/Makefile =================================================================== --- lldb/test/API/macosx/simulator/Makefile +++ lldb/test/API/macosx/simulator/Makefile @@ -1,3 +1,4 @@ C_SOURCES := hello.c include Makefile.rules + Index: lldb/test/API/macosx/simulator/TestSimulatorPlatform.py =================================================================== --- lldb/test/API/macosx/simulator/TestSimulatorPlatform.py +++ lldb/test/API/macosx/simulator/TestSimulatorPlatform.py @@ -11,10 +11,23 @@ mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True - def run_with(self, arch, platform, os, env): - self.build(dictionary={'TRIPLE': arch+'-apple-'+os+'-'+env, 'ARCH': arch}) + def check_load_commands(self, expected_load_command): + """sanity check the built binary for the expected number of load commands""" + load_cmds = subprocess.check_output( + ['otool', '-l', self.getBuildArtifact()] + ).decode("utf-8") + found = 0 + for line in load_cmds.split('\n'): + if expected_load_command in line: + found += 1 + self.assertEquals(found, 1, "wrong load command") + + + def run_with(self, arch, os, env, expected_load_command): + self.build(dictionary={'TRIPLE': arch+'-apple-'+os+'-'+env}) lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("hello.c")) + self.check_load_commands(expected_load_command) self.expect('image list -b -t', patterns=['a\.out '+arch+'-apple-'+os+'.*-'+env]) @@ -25,7 +38,7 @@ """Test running an iOS simulator binary""" self.run_with(arch=self.getArchitecture(), os='ios', env='simulator', - platform='iphonesimulator') + expected_load_command='LC_BUILD_VERSION') @skipUnlessDarwin @skipIfDarwinEmbedded @@ -34,14 +47,103 @@ """Test running an tvOS simulator binary""" self.run_with(arch=self.getArchitecture(), os='tvos', env='simulator', - platform='appletvsimulator') + expected_load_command='LC_BUILD_VERSION') @skipUnlessDarwin @skipIfDarwinEmbedded @apple_simulator_test('watch') @skipIfDarwin # rdar://problem/64552748 - def test_watchos(self): + @skipIf(archs=['arm64','arm64e']) + def test_watchos_i386(self): """Test running a 32-bit watchOS simulator binary""" self.run_with(arch='i386', os='watchos', env='simulator', - platform='watchsimulator') + expected_load_command='LC_BUILD_VERSION') + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('watch') + @skipIfDarwin # rdar://problem/64552748 + @skipIf(archs=['i386','x86_64']) + def test_watchos_armv7k(self): + """Test running a 32-bit watchOS simulator binary""" + self.run_with(arch='armv7k', + os='watchos', env='simulator', + expected_load_command='LC_BUILD_VERSION') + + + # + # Back-deployment tests. + # + # Older Mach-O versions used less expressive load commands, such + # as LC_VERSION_MIN_IPHONEOS that wouldn't distinguish between ios + # and ios-simulator. When targeting a simulator on Apple Silicon + # macOS, however, these legacy load commands are never generated. + # + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('iphone') + @skipIf(archs=['arm64','arm64e']) + def test_lc_version_min_iphoneos(self): + """Test running a back-deploying iOS simulator binary + with a legacy iOS load command""" + self.run_with(arch=self.getArchitecture(), + os='ios11.0', env='simulator', + expected_load_command='LC_VERSION_MIN_IPHONEOS') + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('iphone') + @skipIf(archs=['i386','x86_64']) + def test_ios_backdeploy_apple_silicon(self): + """Test running a back-deploying iOS simulator binary""" + self.run_with(arch=self.getArchitecture(), + os='ios11.0', env='simulator', + expected_load_command='LC_BUILD_VERSION') + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('appletv') + @skipIf(archs=['arm64','arm64e']) + def test_lc_version_min_tvos(self): + """Test running a back-deploying tvOS simulator binary + with a legacy tvOS load command""" + self.run_with(arch=self.getArchitecture(), + os='tvos11.0', env='simulator', + expected_load_command='LC_VERSION_MIN_TVOS') + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('appletv') + @skipIf(archs=['i386','x86_64']) + def test_tvos_backdeploy_apple_silicon(self): + """Test running a back-deploying tvOS simulator binary""" + self.run_with(arch=self.getArchitecture(), + os='tvos11.0', env='simulator', + expected_load_command='LC_BUILD_VERSION') + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('watch') + @skipIf(archs=['arm64','arm64e']) + @skipIfDarwin # rdar://problem/64552748 + def test_lc_version_min_watchos(self): + """Test running a back-deploying watchOS simulator binary + with a legacy watchOS load command""" + self.run_with(arch='i386', + os='watchos4.0', env='simulator', + expected_load_command='LC_VERSION_MIN_WATCHOS') + + @skipUnlessDarwin + @skipIfDarwinEmbedded + @apple_simulator_test('watch') + @skipIf(archs=['arm64','arm64e']) + @skipIfDarwin # rdar://problem/64552748 + def test_watchos_backdeploy_apple_silicon(self): + """Test running a back-deploying watchOS simulator binary""" + self.run_with(arch='armv7k', + os='watchos4.0', env='simulator', + expected_load_command='LC_BUILD_VERSION') + + Index: lldb/tools/debugserver/source/DNB.cpp =================================================================== --- lldb/tools/debugserver/source/DNB.cpp +++ lldb/tools/debugserver/source/DNB.cpp @@ -1392,10 +1392,15 @@ uint32_t& minor_version, uint32_t& patch_version) { MachProcessSP procSP; - if (GetProcessSP(pid, procSP)) - return procSP->GetDeploymentInfo(lc, load_command_address, - major_version, minor_version, - patch_version); + if (GetProcessSP(pid, procSP)) { + // FIXME: This doesn't correct for older ios simulator and macCatalyst. + auto info = + procSP->GetDeploymentInfo(lc, load_command_address); + major_version = info.major_version; + minor_version = info.minor_version; + patch_version = info.patch_version; + return procSP->GetPlatformString(info.platform); + } return nullptr; } Index: lldb/tools/debugserver/source/MacOSX/MachProcess.h =================================================================== --- lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -230,10 +230,21 @@ uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); - const char * - GetDeploymentInfo(const struct load_command&, uint64_t load_command_address, - uint32_t& major_version, uint32_t& minor_version, - uint32_t& patch_version); + + struct DeploymentInfo { + DeploymentInfo() = default; + operator bool() { return platform > 0; } + /// The Mach-O platform type; + unsigned char platform = 0; + /// Pre-LC_BUILD_VERSION files don't disambiguate between ios and ios simulator. + bool maybe_simulator = false; + uint32_t major_version = 0; + uint32_t minor_version = 0; + uint32_t patch_version = 0; + }; + DeploymentInfo GetDeploymentInfo(const struct load_command &, + uint64_t load_command_address); + static const char *GetPlatformString(unsigned char platform); bool GetMachOInformationFromMemory(uint32_t platform, nub_addr_t mach_o_header_addr, int wordsize, Index: lldb/tools/debugserver/source/MacOSX/MachProcess.mm =================================================================== --- lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -601,88 +601,78 @@ plo_pthread_tsd_entry_size); } -/// Determine whether this is running on macOS. -/// Since debugserver runs on the same machine as the process, we can -/// just look at the compilation target. -static bool IsMacOSHost() { -#if TARGET_OS_OSX == 1 - return true; -#else - return false; -#endif -} - -const char *MachProcess::GetDeploymentInfo(const struct load_command& lc, - uint64_t load_command_address, - uint32_t& major_version, - uint32_t& minor_version, - uint32_t& patch_version) { +MachProcess::DeploymentInfo +MachProcess::GetDeploymentInfo(const struct load_command &lc, + uint64_t load_command_address) { + DeploymentInfo info; uint32_t cmd = lc.cmd & ~LC_REQ_DYLD; - bool lc_cmd_known = - cmd == LC_VERSION_MIN_IPHONEOS || cmd == LC_VERSION_MIN_MACOSX || - cmd == LC_VERSION_MIN_TVOS || cmd == LC_VERSION_MIN_WATCHOS; - - if (lc_cmd_known) { + // Handle the older LC_VERSION load commands, which don't + // distinguish between simulator and real hardware. + auto handle_version_min = [&](char platform) { struct version_min_command vers_cmd; if (ReadMemory(load_command_address, sizeof(struct version_min_command), - &vers_cmd) != sizeof(struct version_min_command)) { - return nullptr; - } - major_version = vers_cmd.sdk >> 16; - minor_version = (vers_cmd.sdk >> 8) & 0xffu; - patch_version = vers_cmd.sdk & 0xffu; - - // Handle the older LC_VERSION load commands, which don't - // distinguish between simulator and real hardware. - switch (cmd) { - case LC_VERSION_MIN_IPHONEOS: - return IsMacOSHost() ? "iossimulator": "ios"; - case LC_VERSION_MIN_MACOSX: - return "macosx"; - case LC_VERSION_MIN_TVOS: - return IsMacOSHost() ? "tvossimulator": "tvos"; - case LC_VERSION_MIN_WATCHOS: - return IsMacOSHost() ? "watchossimulator" : "watchos"; - default: - return nullptr; - } - } -#if defined (LC_BUILD_VERSION) - if (cmd == LC_BUILD_VERSION) { + &vers_cmd) != sizeof(struct version_min_command)) + return; + info.platform = platform; + info.major_version = vers_cmd.sdk >> 16; + info.minor_version = (vers_cmd.sdk >> 8) & 0xffu; + info.patch_version = vers_cmd.sdk & 0xffu; + info.maybe_simulator = true; + }; + switch (cmd) { + case LC_VERSION_MIN_IPHONEOS: + handle_version_min(PLATFORM_IOS); + break; + case LC_VERSION_MIN_MACOSX: + handle_version_min(PLATFORM_MACOS); + break; + case LC_VERSION_MIN_TVOS: + handle_version_min(PLATFORM_TVOS); + break; + case LC_VERSION_MIN_WATCHOS: + handle_version_min(PLATFORM_WATCHOS); + break; +#if defined(LC_BUILD_VERSION) + case LC_BUILD_VERSION: { struct build_version_command build_vers; if (ReadMemory(load_command_address, sizeof(struct build_version_command), - &build_vers) != sizeof(struct build_version_command)) { - return nullptr; - } - major_version = build_vers.sdk >> 16;; - minor_version = (build_vers.sdk >> 8) & 0xffu; - patch_version = build_vers.sdk & 0xffu; - - switch (build_vers.platform) { - case PLATFORM_MACOS: - return "macosx"; - case PLATFORM_MACCATALYST: - return "maccatalyst"; - case PLATFORM_IOS: - return "ios"; - case PLATFORM_IOSSIMULATOR: - return "iossimulator"; - case PLATFORM_TVOS: - return "tvos"; - case PLATFORM_TVOSSIMULATOR: - return "tvossimulator"; - case PLATFORM_WATCHOS: - return "watchos"; - case PLATFORM_WATCHOSSIMULATOR: - return "watchossimulator"; - case PLATFORM_BRIDGEOS: - return "bridgeos"; - case PLATFORM_DRIVERKIT: - return "driverkit"; - } + &build_vers) != sizeof(struct build_version_command)) + break; + info.platform = build_vers.platform; + info.major_version = build_vers.sdk >> 16; + info.minor_version = (build_vers.sdk >> 8) & 0xffu; + info.patch_version = build_vers.sdk & 0xffu; + break; } #endif - return nullptr; + } + return info; +} + +const char *MachProcess::GetPlatformString(unsigned char platform) { + switch (platform) { + case PLATFORM_MACOS: + return "macosx"; + case PLATFORM_MACCATALYST: + return "maccatalyst"; + case PLATFORM_IOS: + return "ios"; + case PLATFORM_IOSSIMULATOR: + return "iossimulator"; + case PLATFORM_TVOS: + return "tvos"; + case PLATFORM_TVOSSIMULATOR: + return "tvossimulator"; + case PLATFORM_WATCHOS: + return "watchos"; + case PLATFORM_WATCHOSSIMULATOR: + return "watchossimulator"; + case PLATFORM_BRIDGEOS: + return "bridgeos"; + case PLATFORM_DRIVERKIT: + return "driverkit"; + } + return ""; } // Given an address, read the mach-o header and load commands out of memory to @@ -787,10 +777,36 @@ sizeof(struct uuid_command)) uuid_copy(inf.uuid, uuidcmd.uuid); } - - uint32_t major_version, minor_version, patch_version; - if (const char *lc_platform = GetDeploymentInfo( - lc, load_cmds_p, major_version, minor_version, patch_version)) { + if (DeploymentInfo deployment_info = GetDeploymentInfo(lc, load_cmds_p)) { + // Simulator support. If the platform is ambiguous, use the dyld info. + if (deployment_info.maybe_simulator) { + // If dyld doesn't return a platform, use a heuristic. +#if (defined(__x86_64__) || defined(__i386__)) + // If we are running on Intel macOS, it is safe to assume + // this is really a back-deploying simulator binary. + if (deployment_info.maybe_simulator) { + switch (deployment_info.platform) { + case PLATFORM_IOS: + deployment_info.platform = PLATFORM_IOSSIMULATOR; + break; + case PLATFORM_TVOS: + deployment_info.platform = PLATFORM_TVOSSIMULATOR; + break; + case PLATFORM_WATCHOS: + deployment_info.platform = PLATFORM_WATCHOSSIMULATOR; + break; + } +#else + // On an Apple Silicon macOS host, there is no + // ambiguity. The only binaries that use legacy load + // commands are back-deploying native iOS binaries. All + // simulator binaries use the newer, unambiguous + // LC_BUILD_VERSION load commands. + deployment_info.maybe_simulator = false; +#endif + } + } + const char *lc_platform = GetPlatformString(deployment_info.platform); // macCatalyst support. // // This handles two special cases: @@ -824,12 +840,15 @@ } 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 += + std::to_string(deployment_info.major_version); inf.min_version_os_version += "."; - inf.min_version_os_version += std::to_string(minor_version); - if (patch_version != 0) { + inf.min_version_os_version += + std::to_string(deployment_info.minor_version); + if (deployment_info.patch_version != 0) { inf.min_version_os_version += "."; - inf.min_version_os_version += std::to_string(patch_version); + inf.min_version_os_version += + std::to_string(deployment_info.patch_version); } } }