diff --git a/Sources/JITSession.swift b/Sources/JITSession.swift index cd84b9b..3648cb8 100644 --- a/Sources/JITSession.swift +++ b/Sources/JITSession.swift @@ -42,7 +42,18 @@ final class JITSession { _ = try? sendCommand("QStartNoAckMode", over: debugProxy) debug_proxy_set_ack_mode(debugProxy, 0) - try ScriptRunner(targetPID: targetPID, debugProxy: debugProxy, progress: progress).run() + if ProcessInfo.processInfo.hasTXM { + try ScriptRunner(targetPID: targetPID, debugProxy: debugProxy, progress: progress).run() + } else { + try attachWithoutScript(targetPID: targetPID, debugProxy: debugProxy, progress: progress) + } + } + + private func attachWithoutScript(targetPID: Int32, debugProxy: OpaquePointer, progress: (String) -> Void) throws { + progress("Attaching to pid \(targetPID). This device has no TXM, so the attach alone enables JIT and the script is skipped.") + _ = try sendCommand("vAttach;\(String(targetPID, radix: 16))", over: debugProxy) + _ = try? sendCommand("D", over: debugProxy) + progress("JIT enabled (debugger attached and detached).") } private func openPairingFile() throws -> OpaquePointer { diff --git a/Sources/ProcessInfo+TXM.swift b/Sources/ProcessInfo+TXM.swift new file mode 100644 index 0000000..89de14d --- /dev/null +++ b/Sources/ProcessInfo+TXM.swift @@ -0,0 +1,58 @@ +import Foundation + +extension ProcessInfo { + var hasTXM: Bool { + let hardware = Self.hardwareIdentifier() + + if Self.isIOS27OrNewer { + return hardware != "iPad8,11" && hardware != "iPad8,12" + } + + if Self.isIOS26OrNewer { + return Self.hasTXMSupport(hardwareIdentifier: hardware) + } + + return false + } + + private static func hasTXMSupport(hardwareIdentifier: String) -> Bool { + let firstTXM = 14.2 + let iPadTXM = 14.5 + guard let version = deviceVersion(from: hardwareIdentifier) else { + return false + } + if hardwareIdentifier.hasPrefix("iPad") { + return version >= iPadTXM + } + return version >= firstTXM + } + + private static func deviceVersion(from identifier: String) -> Double? { + func version(forPrefix prefix: String) -> Double? { + guard identifier.hasPrefix(prefix) else { return nil } + let parts = identifier.dropFirst(prefix.count).split(separator: ",") + guard parts.count == 2, let major = Int(parts[0]), let minor = Int(parts[1]) else { return nil } + return Double("\(major).\(minor)") + } + return version(forPrefix: "iPhone") ?? version(forPrefix: "iPad") + } + + private static func hardwareIdentifier() -> String { + var size = 0 + sysctlbyname("hw.machine", nil, &size, nil, 0) + guard size > 0 else { return "" } + var buffer = [CChar](repeating: 0, count: size) + sysctlbyname("hw.machine", &buffer, &size, nil, 0) + return String(cString: buffer) + } + + private static var isIOS26OrNewer: Bool { + if #available(iOS 26.0, *) { return true } + return false + } + + private static var isIOS27OrNewer: Bool { + if #available(iOS 27.0, *) { return true } + return false + } +}