mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Move the backtracing code into a new Runtime module. This means renaming the Swift Runtime's CMake target because otherwise there will be a name clash. rdar://124913332
1023 lines
30 KiB
Swift
1023 lines
30 KiB
Swift
//===--- Context.swift - Unwind context structure -------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2022 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines the Context protocol and some concrete implementations for various
|
|
// different types of CPU.
|
|
//
|
|
// Context holds register values during unwinding.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Swift
|
|
|
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
|
internal import Darwin
|
|
#elseif os(Windows)
|
|
internal import ucrt
|
|
#elseif canImport(Glibc)
|
|
internal import Glibc
|
|
#elseif canImport(Musl)
|
|
internal import Musl
|
|
#endif
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
internal import BacktracingImpl.OS.Darwin
|
|
#endif
|
|
|
|
internal import BacktracingImpl.FixedLayout
|
|
|
|
typealias x86_64_gprs = swift.runtime.backtrace.x86_64_gprs
|
|
typealias i386_gprs = swift.runtime.backtrace.i386_gprs
|
|
typealias arm64_gprs = swift.runtime.backtrace.arm64_gprs
|
|
typealias arm_gprs = swift.runtime.backtrace.arm_gprs
|
|
|
|
@_spi(Contexts) public enum ContextError: Error {
|
|
case unableToFormTLSAddress
|
|
}
|
|
|
|
@_spi(Contexts) public protocol Context: CustomStringConvertible {
|
|
/// Represents a machine address for this type of machine
|
|
associatedtype Address: FixedWidthInteger
|
|
|
|
/// Represents a size for this type of machine
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
/// The type of a general purpose register on this machine
|
|
associatedtype GPRValue: FixedWidthInteger
|
|
|
|
/// An enumerated type defining the registers for the machine (this comes
|
|
/// from the architecture specific DWARF specification).
|
|
associatedtype Register: RawRepresentable where Register.RawValue == Int
|
|
|
|
/// The architecture tag for this context (e.g. arm64, x86_64)
|
|
var architecture: String { get }
|
|
|
|
/// The program counter; this is likely a return address
|
|
var programCounter: GPRValue { get set }
|
|
|
|
/// The stack pointer
|
|
var stackPointer: GPRValue { get set }
|
|
|
|
/// The frame pointer
|
|
var framePointer: GPRValue { get set }
|
|
|
|
/// The CFA as defined by the relevant architecture specific DWARF
|
|
/// specification. For the architectures we have currently, it turns out
|
|
/// that this is the stack pointer, but it might in general be some other
|
|
/// thing.
|
|
var callFrameAddress: GPRValue { get set }
|
|
|
|
/// The number of register slots to reserve in the unwinder (this corresponds
|
|
/// to the DWARF register numbers, which is why some of these reserve a lot
|
|
/// of slots).
|
|
static var registerCount: Int { get }
|
|
|
|
/// Given a thread local address, form a genuine machine address
|
|
func formTLSAddress(threadLocal: Address) throws -> Address
|
|
|
|
/// Get the value of the specified general purpose register, or nil if unknown
|
|
func getRegister(_ register: Register) -> GPRValue?
|
|
|
|
/// Set the value of the specified general purpose register (or mark it as
|
|
/// unknown if nil is passed)
|
|
mutating func setRegister(_ register: Register, to value: GPRValue?)
|
|
|
|
/// Set all of the registers in bulk
|
|
mutating func setRegisters(_ registers: [GPRValue?])
|
|
|
|
/// Strip any pointer authentication that might apply from an address.
|
|
static func stripPtrAuth(address: Address) -> Address
|
|
|
|
/// Test if an address is appropriately aligned for the stack.
|
|
static func isAlignedForStack(framePointer: Address) -> Bool
|
|
}
|
|
|
|
extension Context {
|
|
public func formTLSAddress(threadLocal: Address) throws -> Address {
|
|
throw ContextError.unableToFormTLSAddress
|
|
}
|
|
|
|
public mutating func setRegisters(_ registers: [GPRValue?]) {
|
|
for (ndx, value) in registers.enumerated() {
|
|
if let reg = Register(rawValue: ndx) {
|
|
setRegister(reg, to: value)
|
|
}
|
|
}
|
|
}
|
|
|
|
public static func stripPtrAuth(address: Address) -> Address {
|
|
return address
|
|
}
|
|
}
|
|
|
|
// .. Extensions to the GPR structures .........................................
|
|
|
|
// We need these because the arrays in the _gprs structs (which are defined
|
|
// in C so that the layout is fixed) get imported as tuples.
|
|
|
|
extension x86_64_gprs {
|
|
func getR(_ ndx: Int) -> UInt64 {
|
|
return withUnsafePointer(to: _r) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 16) {
|
|
$0[ndx]
|
|
}
|
|
}
|
|
}
|
|
|
|
mutating func setR(_ ndx: Int, to value: UInt64) {
|
|
withUnsafeMutablePointer(to: &_r) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 16) {
|
|
$0[ndx] = value
|
|
}
|
|
}
|
|
valid |= 1 << ndx
|
|
}
|
|
}
|
|
|
|
extension i386_gprs {
|
|
func getR(_ ndx: Int) -> UInt32 {
|
|
return withUnsafePointer(to: _r) {
|
|
$0.withMemoryRebound(to: UInt32.self, capacity: 8) {
|
|
$0[ndx]
|
|
}
|
|
}
|
|
}
|
|
|
|
mutating func setR(_ ndx: Int, to value: UInt32) {
|
|
withUnsafeMutablePointer(to: &_r) {
|
|
$0.withMemoryRebound(to: UInt32.self, capacity: 8) {
|
|
$0[ndx] = value
|
|
}
|
|
}
|
|
valid |= 1 << ndx
|
|
}
|
|
}
|
|
|
|
extension arm64_gprs {
|
|
func getX(_ ndx: Int) -> UInt64 {
|
|
return withUnsafePointer(to: _x) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 32) {
|
|
$0[ndx]
|
|
}
|
|
}
|
|
}
|
|
|
|
mutating func setX(_ ndx: Int, to value: UInt64) {
|
|
withUnsafeMutablePointer(to: &_x) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 32) {
|
|
$0[ndx] = value
|
|
}
|
|
}
|
|
valid |= 1 << ndx
|
|
}
|
|
}
|
|
|
|
extension arm_gprs {
|
|
func getR(_ ndx: Int) -> UInt32 {
|
|
return withUnsafePointer(to: _r) {
|
|
$0.withMemoryRebound(to: UInt32.self, capacity: 16) {
|
|
$0[ndx]
|
|
}
|
|
}
|
|
}
|
|
|
|
mutating func setR(_ ndx: Int, to value: UInt32) {
|
|
withUnsafeMutablePointer(to: &_r) {
|
|
$0.withMemoryRebound(to: UInt32.self, capacity: 16) {
|
|
$0[ndx] = value
|
|
}
|
|
}
|
|
valid |= 1 << ndx
|
|
}
|
|
}
|
|
|
|
// .. x86-64 ...................................................................
|
|
|
|
@_spi(Contexts) public struct X86_64Context: Context {
|
|
public typealias Address = UInt64
|
|
public typealias Size = UInt64
|
|
public typealias GPRValue = UInt64
|
|
public typealias Register = X86_64Register
|
|
|
|
var gprs = x86_64_gprs()
|
|
|
|
public var architecture: String { "x86_64" }
|
|
|
|
public var programCounter: Address {
|
|
get { return gprs.rip }
|
|
set {
|
|
gprs.rip = newValue
|
|
gprs.valid |= 1 << 20
|
|
}
|
|
}
|
|
public var framePointer: Address {
|
|
get { return gprs.getR(X86_64Register.rbp.rawValue) }
|
|
set {
|
|
gprs.setR(X86_64Register.rbp.rawValue, to: newValue)
|
|
}
|
|
}
|
|
public var stackPointer: Address {
|
|
get { return gprs.getR(X86_64Register.rsp.rawValue) }
|
|
set {
|
|
gprs.setR(X86_64Register.rsp.rawValue, to: newValue)
|
|
}
|
|
}
|
|
|
|
public var callFrameAddress: GPRValue {
|
|
get { return stackPointer }
|
|
set { stackPointer = newValue }
|
|
}
|
|
|
|
public static var registerCount: Int { return 56 }
|
|
|
|
#if os(macOS) && arch(x86_64)
|
|
init?(from thread: thread_t) {
|
|
var state = darwin_x86_64_thread_state()
|
|
let kr = thread_get_state(thread,
|
|
X86_THREAD_STATE64,
|
|
&state)
|
|
if kr != KERN_SUCCESS {
|
|
return nil
|
|
}
|
|
|
|
self.init(from: state)
|
|
}
|
|
|
|
init(with mctx: darwin_x86_64_mcontext) {
|
|
self.init(from: mctx.ss)
|
|
}
|
|
|
|
init(from state: darwin_x86_64_thread_state) {
|
|
gprs.setR(X86_64Register.rax.rawValue, to: state.rax)
|
|
gprs.setR(X86_64Register.rbx.rawValue, to: state.rbx)
|
|
gprs.setR(X86_64Register.rcx.rawValue, to: state.rcx)
|
|
gprs.setR(X86_64Register.rdx.rawValue, to: state.rdx)
|
|
gprs.setR(X86_64Register.rdi.rawValue, to: state.rdi)
|
|
gprs.setR(X86_64Register.rsi.rawValue, to: state.rsi)
|
|
gprs.setR(X86_64Register.rbp.rawValue, to: state.rbp)
|
|
gprs.setR(X86_64Register.rsp.rawValue, to: state.rsp)
|
|
gprs.setR(X86_64Register.r8.rawValue, to: state.r8)
|
|
gprs.setR(X86_64Register.r9.rawValue, to: state.r9)
|
|
gprs.setR(X86_64Register.r10.rawValue, to: state.r10)
|
|
gprs.setR(X86_64Register.r11.rawValue, to: state.r11)
|
|
gprs.setR(X86_64Register.r12.rawValue, to: state.r12)
|
|
gprs.setR(X86_64Register.r13.rawValue, to: state.r13)
|
|
gprs.setR(X86_64Register.r14.rawValue, to: state.r14)
|
|
gprs.setR(X86_64Register.r15.rawValue, to: state.r15)
|
|
gprs.rip = state.rip
|
|
gprs.rflags = state.rflags
|
|
gprs.cs = UInt16(state.cs)
|
|
gprs.fs = UInt16(state.fs)
|
|
gprs.gs = UInt16(state.gs)
|
|
gprs.valid = 0x1fffff
|
|
}
|
|
|
|
public static func fromHostThread(_ thread: Any) -> HostContext? {
|
|
return X86_64Context(from: thread as! thread_t)
|
|
}
|
|
|
|
public static func fromHostMContext(_ mcontext: Any) -> HostContext {
|
|
return X86_64Context(with: mcontext as! darwin_x86_64_mcontext)
|
|
}
|
|
#elseif os(Linux) && arch(x86_64)
|
|
init(with mctx: mcontext_t) {
|
|
gprs.setR(X86_64Register.rax.rawValue, to: UInt64(bitPattern: mctx.gregs.13))
|
|
gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(bitPattern: mctx.gregs.12))
|
|
gprs.setR(X86_64Register.rcx.rawValue, to: UInt64(bitPattern: mctx.gregs.14))
|
|
gprs.setR(X86_64Register.rdx.rawValue, to: UInt64(bitPattern: mctx.gregs.11))
|
|
gprs.setR(X86_64Register.rdi.rawValue, to: UInt64(bitPattern: mctx.gregs.9))
|
|
gprs.setR(X86_64Register.rsi.rawValue, to: UInt64(bitPattern: mctx.gregs.8))
|
|
gprs.setR(X86_64Register.rbp.rawValue, to: UInt64(bitPattern: mctx.gregs.10))
|
|
gprs.setR(X86_64Register.rsp.rawValue, to: UInt64(bitPattern: mctx.gregs.15))
|
|
gprs.setR(X86_64Register.r8.rawValue, to: UInt64(bitPattern: mctx.gregs.0))
|
|
gprs.setR(X86_64Register.r9.rawValue, to: UInt64(bitPattern: mctx.gregs.1))
|
|
gprs.setR(X86_64Register.r10.rawValue, to: UInt64(bitPattern: mctx.gregs.2))
|
|
gprs.setR(X86_64Register.r11.rawValue, to: UInt64(bitPattern: mctx.gregs.3))
|
|
gprs.setR(X86_64Register.r12.rawValue, to: UInt64(bitPattern: mctx.gregs.4))
|
|
gprs.setR(X86_64Register.r13.rawValue, to: UInt64(bitPattern: mctx.gregs.5))
|
|
gprs.setR(X86_64Register.r14.rawValue, to: UInt64(bitPattern: mctx.gregs.6))
|
|
gprs.setR(X86_64Register.r15.rawValue, to: UInt64(bitPattern: mctx.gregs.7))
|
|
gprs.rip = UInt64(bitPattern: mctx.gregs.16)
|
|
gprs.rflags = UInt64(bitPattern: mctx.gregs.17)
|
|
gprs.cs = UInt16(mctx.gregs.18 & 0xffff)
|
|
gprs.fs = UInt16((mctx.gregs.18 >> 16) & 0xffff)
|
|
gprs.gs = UInt16((mctx.gregs.18 >> 32) & 0xffff)
|
|
gprs.valid = 0x1fffff
|
|
}
|
|
|
|
public static func fromHostMContext(_ mcontext: Any) -> HostContext {
|
|
return X86_64Context(with: mcontext as! mcontext_t)
|
|
}
|
|
#endif
|
|
|
|
#if os(Windows) || !SWIFT_ASM_AVAILABLE
|
|
struct NotImplemented: Error {}
|
|
public static func withCurrentContext<T>(fn: (X86_64Context) throws -> T) throws -> T {
|
|
throw NotImplemented()
|
|
}
|
|
#elseif arch(x86_64)
|
|
@usableFromInline
|
|
@_silgen_name("_swift_get_cpu_context")
|
|
static func _swift_get_cpu_context() -> X86_64Context
|
|
|
|
@_transparent
|
|
public static func withCurrentContext<T>(fn: (X86_64Context) throws -> T) rethrows -> T {
|
|
return try fn(_swift_get_cpu_context())
|
|
}
|
|
#endif
|
|
|
|
private func validNdx(_ register: Register) -> Int? {
|
|
switch register {
|
|
case .rax ... .r15:
|
|
return register.rawValue
|
|
case .rflags:
|
|
return 16
|
|
case .cs:
|
|
return 17
|
|
case .fs:
|
|
return 18
|
|
case .gs:
|
|
return 19
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private func isValid(_ register: Register) -> Bool {
|
|
guard let ndx = validNdx(register) else {
|
|
return false
|
|
}
|
|
return (gprs.valid & (UInt64(1) << ndx)) != 0
|
|
}
|
|
|
|
private mutating func setValid(_ register: Register) {
|
|
guard let ndx = validNdx(register) else {
|
|
return
|
|
}
|
|
gprs.valid |= UInt64(1) << ndx
|
|
}
|
|
|
|
private mutating func clearValid(_ register: Register) {
|
|
guard let ndx = validNdx(register) else {
|
|
return
|
|
}
|
|
gprs.valid &= ~(UInt64(1) << ndx)
|
|
}
|
|
|
|
public func getRegister(_ register: Register) -> GPRValue? {
|
|
if !isValid(register) {
|
|
return nil
|
|
}
|
|
|
|
switch register {
|
|
case .rax ... .r15:
|
|
return gprs.getR(register.rawValue)
|
|
case .rflags: return gprs.rflags
|
|
case .cs: return UInt64(gprs.cs)
|
|
case .fs: return UInt64(gprs.fs)
|
|
case .gs: return UInt64(gprs.gs)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public mutating func setRegister(_ register: Register, to value: GPRValue?) {
|
|
if let value = value {
|
|
switch register {
|
|
case .rax ... .r15:
|
|
gprs.setR(register.rawValue, to: value)
|
|
case .rflags:
|
|
gprs.rflags = value
|
|
setValid(register)
|
|
case .cs:
|
|
gprs.cs = UInt16(value)
|
|
setValid(register)
|
|
case .fs:
|
|
gprs.fs = UInt16(value)
|
|
setValid(register)
|
|
case .gs:
|
|
gprs.gs = UInt16(value)
|
|
setValid(register)
|
|
default:
|
|
return
|
|
}
|
|
} else {
|
|
clearValid(register)
|
|
}
|
|
}
|
|
|
|
public var description: String {
|
|
return """
|
|
rax: \(hex(gprs.getR(0))) rbx: \(hex(gprs.getR(3))) rcx: \(hex(gprs.getR(2)))
|
|
rdx: \(hex(gprs.getR(1))) rsi: \(hex(gprs.getR(4))) rdi: \(hex(gprs.getR(5)))
|
|
rbp: \(hex(gprs.getR(6))) rsp: \(hex(gprs.getR(7))) r8: \(hex(gprs.getR(8)))
|
|
r9: \(hex(gprs.getR(9))) r10: \(hex(gprs.getR(10))) r11: \(hex(gprs.getR(11)))
|
|
r12: \(hex(gprs.getR(12))) r13: \(hex(gprs.getR(13))) r14: \(hex(gprs.getR(14)))
|
|
r15: \(hex(gprs.getR(15)))
|
|
|
|
cs: \(hex(gprs.cs)) fs: \(hex(gprs.fs)) gs: \(hex(gprs.gs))
|
|
|
|
rip: \(hex(gprs.rip)) rflags: \(hex(gprs.rflags))
|
|
"""
|
|
}
|
|
|
|
public static func isAlignedForStack(framePointer: Address) -> Bool {
|
|
return (framePointer & 0xf) == 0
|
|
}
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
internal static var coreSymbolicationArchitecture: CSArchitecture {
|
|
return kCSArchitectureX86_64
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// .. i386 .....................................................................
|
|
|
|
@_spi(Contexts) public struct I386Context: Context {
|
|
public typealias Address = UInt32
|
|
public typealias Size = UInt32
|
|
public typealias GPRValue = UInt32
|
|
public typealias Register = I386Register
|
|
|
|
var gprs = i386_gprs()
|
|
|
|
public var architecture: String { "i386" }
|
|
|
|
public var programCounter: GPRValue {
|
|
get { return gprs.eip }
|
|
set {
|
|
gprs.eip = newValue
|
|
gprs.valid |= 1 << 15
|
|
}
|
|
}
|
|
|
|
public var framePointer: GPRValue {
|
|
get { return gprs.getR(I386Register.ebp.rawValue) }
|
|
set { gprs.setR(I386Register.ebp.rawValue, to: newValue) }
|
|
}
|
|
|
|
public var stackPointer: GPRValue {
|
|
get { return gprs.getR(I386Register.esp.rawValue) }
|
|
set { gprs.setR(I386Register.esp.rawValue, to: newValue) }
|
|
}
|
|
|
|
public var callFrameAddress: GPRValue {
|
|
get { return stackPointer }
|
|
set { stackPointer = newValue }
|
|
}
|
|
|
|
public static var registerCount: Int { return 50 }
|
|
|
|
#if os(Linux) && arch(i386)
|
|
init(with mctx: mcontext_t) {
|
|
gprs.setR(I386Register.eax.rawValue, to: UInt32(bitPattern: mctx.gregs.11))
|
|
gprs.setR(I386Register.ecx.rawValue, to: UInt32(bitPattern: mctx.gregs.10))
|
|
gprs.setR(I386Register.edx.rawValue, to: UInt32(bitPattern: mctx.gregs.9))
|
|
gprs.setR(I386Register.ebx.rawValue, to: UInt32(bitPattern: mctx.gregs.8))
|
|
gprs.setR(I386Register.esp.rawValue, to: UInt32(bitPattern: mctx.gregs.7))
|
|
gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.6))
|
|
gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.5))
|
|
gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.4))
|
|
gprs.eip = UInt32(bitPattern: mctx.gregs.14)
|
|
gprs.eflags = UInt32(bitPattern: mctx.gregs.16)
|
|
gprs.segreg.0 = UInt16(bitPattern: mctx.gregs.2 & 0xffff) // es
|
|
gprs.segreg.1 = UInt16(bitPattern: mctx.gregs.15 & 0xffff) // cs
|
|
gprs.segreg.2 = UInt16(bitPattern: mctx.gregs.18 & 0xffff) // ss
|
|
gprs.segreg.3 = UInt16(bitPattern: mctx.gregs.3 & 0xffff) // ds
|
|
gprs.segreg.4 = UInt16(bitPattern: mctx.gregs.1 & 0xffff) // fs
|
|
gprs.segreg.5 = UInt16(bitPattern: mctx.gregs.0 & 0xffff) // gs
|
|
gprs.valid = 0x7fff
|
|
}
|
|
|
|
public static func fromHostMContext(_ mcontext: Any) -> HostContext {
|
|
return I386Context(with: mcontext as! mcontext_t)
|
|
}
|
|
#endif
|
|
|
|
#if os(Windows) || !SWIFT_ASM_AVAILABLE
|
|
struct NotImplemented: Error {}
|
|
public static func withCurrentContext<T>(fn: (I386Context) throws -> T) throws -> T {
|
|
throw NotImplemented()
|
|
}
|
|
#elseif arch(i386)
|
|
@usableFromInline
|
|
@_silgen_name("_swift_get_cpu_context")
|
|
static func _swift_get_cpu_context() -> I386Context
|
|
|
|
@_transparent
|
|
public static func withCurrentContext<T>(fn: (I386Context) throws -> T) rethrows -> T {
|
|
return try fn(_swift_get_cpu_context())
|
|
}
|
|
#endif
|
|
|
|
private func validNdx(_ register: Register) -> Int? {
|
|
switch register {
|
|
case .eax ... .edi:
|
|
return register.rawValue
|
|
case .eflags:
|
|
return 8
|
|
case .es, .cs, .ss, .ds, .fs, .gs:
|
|
return 9 + register.rawValue - Register.es.rawValue
|
|
case .ra:
|
|
return 15
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private func isValid(_ register: Register) -> Bool {
|
|
guard let ndx = validNdx(register) else {
|
|
return false
|
|
}
|
|
return (gprs.valid & (UInt32(1) << ndx)) != 0
|
|
}
|
|
|
|
private mutating func setValid(_ register: Register) {
|
|
guard let ndx = validNdx(register) else {
|
|
return
|
|
}
|
|
gprs.valid |= UInt32(1) << ndx
|
|
}
|
|
|
|
private mutating func clearValid(_ register: Register) {
|
|
guard let ndx = validNdx(register) else {
|
|
return
|
|
}
|
|
gprs.valid &= ~(UInt32(1) << ndx)
|
|
}
|
|
|
|
public func getRegister(_ register: Register) -> GPRValue? {
|
|
if !isValid(register) {
|
|
return nil
|
|
}
|
|
switch register {
|
|
case .eax ... .edi:
|
|
return gprs.getR(register.rawValue)
|
|
case .eflags: return gprs.eflags
|
|
case .es ... .gs:
|
|
return withUnsafeBytes(of: gprs.segreg) { ptr in
|
|
return ptr.withMemoryRebound(to: GPRValue.self) { regs in
|
|
return regs[register.rawValue - Register.es.rawValue]
|
|
}
|
|
}
|
|
case .ra: return gprs.eip
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public mutating func setRegister(_ register: Register, to value: GPRValue?) {
|
|
if let value = value {
|
|
switch register {
|
|
case .eax ... .edi:
|
|
gprs.setR(register.rawValue, to: value)
|
|
case .eflags:
|
|
gprs.eflags = value
|
|
setValid(register)
|
|
case .es ... .gs:
|
|
withUnsafeMutableBytes(of: &gprs.segreg) { ptr in
|
|
ptr.withMemoryRebound(to: GPRValue.self) { regs in
|
|
regs[register.rawValue - Register.es.rawValue] = value
|
|
}
|
|
}
|
|
setValid(register)
|
|
case .ra:
|
|
gprs.eip = value
|
|
setValid(register)
|
|
default:
|
|
return
|
|
}
|
|
} else {
|
|
clearValid(register)
|
|
}
|
|
}
|
|
|
|
public var description: String {
|
|
return """
|
|
eax: \(hex(gprs.getR(0))) ebx: \(hex(gprs.getR(3))) ecx: \(hex(gprs.getR(1))) edx: \(hex(gprs.getR(2)))
|
|
esi: \(hex(gprs.getR(6))) edi: \(hex(gprs.getR(7))) ebp: \(hex(gprs.getR(5))) esp: \(hex(gprs.getR(4)))
|
|
|
|
es: \(hex(gprs.segreg.0)) cs: \(hex(gprs.segreg.1)) ss: \(hex(gprs.segreg.2)) ds: \(hex(gprs.segreg.3)) fs: \(hex(gprs.segreg.4)) gs: \(hex(gprs.segreg.5))
|
|
|
|
eip: \(hex(gprs.eip)) eflags: \(hex(gprs.eflags))
|
|
"""
|
|
}
|
|
|
|
public static func isAlignedForStack(framePointer: Address) -> Bool {
|
|
return (framePointer & 0xf) == 8
|
|
}
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
internal static var coreSymbolicationArchitecture: CSArchitecture {
|
|
return kCSArchitectureI386
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// .. ARM64 ....................................................................
|
|
|
|
@_spi(Contexts) public struct ARM64Context: Context {
|
|
public typealias Address = UInt64
|
|
public typealias Size = UInt64
|
|
public typealias GPRValue = UInt64
|
|
public typealias Register = ARM64Register
|
|
|
|
var gprs = arm64_gprs()
|
|
|
|
public var architecture: String { "arm64" }
|
|
|
|
public var programCounter: GPRValue {
|
|
get { return gprs.pc }
|
|
set {
|
|
gprs.pc = newValue
|
|
gprs.valid |= 1 << 32
|
|
}
|
|
}
|
|
|
|
public var stackPointer: GPRValue {
|
|
get { return gprs.getX(ARM64Register.sp.rawValue) }
|
|
set {
|
|
gprs.setX(ARM64Register.sp.rawValue, to: newValue)
|
|
}
|
|
}
|
|
|
|
public var framePointer: GPRValue {
|
|
get { return gprs.getX(ARM64Register.x29.rawValue) }
|
|
set {
|
|
gprs.setX(ARM64Register.x29.rawValue, to: newValue)
|
|
}
|
|
}
|
|
|
|
public var callFrameAddress: GPRValue {
|
|
get { return stackPointer }
|
|
set { stackPointer = newValue }
|
|
}
|
|
|
|
public static var registerCount: Int { return 40 }
|
|
|
|
#if os(macOS) && arch(arm64)
|
|
init?(from thread: thread_t) {
|
|
var state = darwin_arm64_thread_state()
|
|
let kr = thread_get_state(thread,
|
|
ARM_THREAD_STATE64,
|
|
&state)
|
|
if kr != KERN_SUCCESS {
|
|
return nil
|
|
}
|
|
|
|
self.init(from: state)
|
|
}
|
|
|
|
init(with mctx: darwin_arm64_mcontext) {
|
|
self.init(from: mctx.ss)
|
|
}
|
|
|
|
init(from state: darwin_arm64_thread_state) {
|
|
withUnsafeMutablePointer(to: &gprs._x) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 32){ to in
|
|
withUnsafePointer(to: state._x) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 29){ from in
|
|
for n in 0..<29 {
|
|
to[n] = from[n]
|
|
}
|
|
}
|
|
}
|
|
|
|
to[29] = state.fp
|
|
to[30] = state.lr
|
|
to[31] = state.sp
|
|
}
|
|
}
|
|
gprs.pc = state.pc
|
|
gprs.valid = 0x1ffffffff
|
|
}
|
|
|
|
public static func fromHostThread(_ thread: Any) -> HostContext? {
|
|
return ARM64Context(from: thread as! thread_t)
|
|
}
|
|
|
|
public static func fromHostMContext(_ mcontext: Any) -> HostContext {
|
|
return ARM64Context(with: mcontext as! darwin_arm64_mcontext)
|
|
}
|
|
#elseif os(Linux) && arch(arm64)
|
|
init(with mctx: mcontext_t) {
|
|
withUnsafeMutablePointer(to: &gprs._x) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 32){ to in
|
|
withUnsafePointer(to: mctx.regs) {
|
|
$0.withMemoryRebound(to: UInt64.self, capacity: 31) { from in
|
|
for n in 0..<31 {
|
|
to[n] = from[n]
|
|
}
|
|
}
|
|
}
|
|
|
|
to[31] = UInt64(mctx.sp)
|
|
}
|
|
}
|
|
gprs.pc = UInt64(mctx.pc)
|
|
gprs.valid = 0x1ffffffff
|
|
}
|
|
|
|
public static func fromHostMContext(_ mcontext: Any) -> HostContext {
|
|
return ARM64Context(with: mcontext as! mcontext_t)
|
|
}
|
|
#endif
|
|
|
|
#if os(Windows) || !SWIFT_ASM_AVAILABLE
|
|
struct NotImplemented: Error {}
|
|
public static func withCurrentContext<T>(fn: (ARM64Context) throws -> T) throws -> T {
|
|
throw NotImplemented()
|
|
}
|
|
#elseif arch(arm64) || arch(arm64_32)
|
|
@usableFromInline
|
|
@_silgen_name("_swift_get_cpu_context")
|
|
static func _swift_get_cpu_context() -> ARM64Context
|
|
|
|
@_transparent
|
|
public static func withCurrentContext<T>(fn: (ARM64Context) throws -> T) rethrows -> T {
|
|
return try fn(_swift_get_cpu_context())
|
|
}
|
|
#endif
|
|
|
|
private func isValid(_ register: Register) -> Bool {
|
|
if register.rawValue < 33 {
|
|
return (gprs.valid & (UInt64(1) << register.rawValue)) != 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
private mutating func setValid(_ register: Register) {
|
|
if register.rawValue < 33 {
|
|
gprs.valid |= UInt64(1) << register.rawValue
|
|
}
|
|
}
|
|
|
|
private mutating func clearValid(_ register: Register) {
|
|
if register.rawValue < 33 {
|
|
gprs.valid &= ~(UInt64(1) << register.rawValue)
|
|
}
|
|
}
|
|
|
|
public func getRegister(_ reg: Register) -> GPRValue? {
|
|
if !isValid(reg) {
|
|
return nil
|
|
}
|
|
switch reg {
|
|
case .x0 ... .sp:
|
|
return gprs.getX(reg.rawValue)
|
|
case .pc:
|
|
return gprs.pc
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public mutating func setRegister(_ reg: Register, to value: GPRValue?) {
|
|
if let value = value {
|
|
switch reg {
|
|
case .x0 ... .sp:
|
|
gprs.setX(reg.rawValue, to: value)
|
|
case .pc:
|
|
gprs.pc = value
|
|
setValid(reg)
|
|
default:
|
|
break
|
|
}
|
|
} else {
|
|
clearValid(reg)
|
|
}
|
|
}
|
|
|
|
public var description: String {
|
|
return """
|
|
x0: \(hex(gprs.getX(0))) x1: \(hex(gprs.getX(1)))
|
|
x2: \(hex(gprs.getX(2))) x3: \(hex(gprs.getX(3)))
|
|
x4: \(hex(gprs.getX(4))) x5: \(hex(gprs.getX(5)))
|
|
x6: \(hex(gprs.getX(6))) x7: \(hex(gprs.getX(7)))
|
|
x8: \(hex(gprs.getX(8))) x9: \(hex(gprs.getX(9)))
|
|
x10: \(hex(gprs.getX(10))) x11: \(hex(gprs.getX(11)))
|
|
x12: \(hex(gprs.getX(12))) x13: \(hex(gprs.getX(13)))
|
|
x14: \(hex(gprs.getX(14))) x15: \(hex(gprs.getX(15)))
|
|
x16: \(hex(gprs.getX(16))) x17: \(hex(gprs.getX(17)))
|
|
x18: \(hex(gprs.getX(18))) x19: \(hex(gprs.getX(19)))
|
|
x20: \(hex(gprs.getX(20))) x21: \(hex(gprs.getX(21)))
|
|
x22: \(hex(gprs.getX(22))) x23: \(hex(gprs.getX(23)))
|
|
x24: \(hex(gprs.getX(24))) x25: \(hex(gprs.getX(25)))
|
|
x26: \(hex(gprs.getX(26))) x27: \(hex(gprs.getX(27)))
|
|
x28: \(hex(gprs.getX(28)))
|
|
|
|
fp: \(hex(gprs.getX(29))) (aka x29)
|
|
lr: \(hex(gprs.getX(30))) (aka x30)
|
|
sp: \(hex(gprs.getX(31))) (aka x31)
|
|
|
|
pc: \(hex(gprs.pc))
|
|
"""
|
|
}
|
|
|
|
public static func isAlignedForStack(framePointer: Address) -> Bool {
|
|
return (framePointer & 1) == 0
|
|
}
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
public static func stripPtrAuth(address: Address) -> Address {
|
|
// Is there a better way to do this? It'd be easy if we just wanted to
|
|
// strip for the *host*, but we might conceivably want this under other
|
|
// circumstances too.
|
|
return address & 0x00007fffffffffff
|
|
}
|
|
|
|
internal static var coreSymbolicationArchitecture: CSArchitecture {
|
|
return kCSArchitectureArm64
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// .. 32-bit ARM ...............................................................
|
|
|
|
@_spi(Contexts) public struct ARMContext: Context {
|
|
public typealias Address = UInt32
|
|
public typealias Size = UInt32
|
|
public typealias GPRValue = UInt32
|
|
public typealias Register = ARMRegister
|
|
|
|
var gprs = arm_gprs()
|
|
|
|
public var architecture: String { "arm" }
|
|
|
|
public var programCounter: GPRValue {
|
|
get { return gprs.getR(ARMRegister.r15.rawValue) }
|
|
set { gprs.setR(ARMRegister.r15.rawValue, to: newValue) }
|
|
}
|
|
|
|
public var stackPointer: GPRValue {
|
|
get { return gprs.getR(ARMRegister.r13.rawValue) }
|
|
set { gprs.setR(ARMRegister.r13.rawValue, to: newValue) }
|
|
}
|
|
|
|
public var framePointer: GPRValue {
|
|
get { return gprs.getR(ARMRegister.r11.rawValue) }
|
|
set { gprs.setR(ARMRegister.r11.rawValue, to: newValue) }
|
|
}
|
|
|
|
public var callFrameAddress: GPRValue {
|
|
get { return stackPointer }
|
|
set { stackPointer = newValue }
|
|
}
|
|
|
|
public static var registerCount: Int { return 16 }
|
|
|
|
#if os(Linux) && arch(arm)
|
|
init(with mctx: mcontext_t) {
|
|
withUnsafeMutablePointer(to: &gprs._r) {
|
|
$0.withMemoryRebound(to: UInt32.self, capacity: 16) {
|
|
withUnsafePointer(to: &mctx.arm_r0) {
|
|
$0.withMemoryRebound(to: UInt32.self, capacity: 16) {
|
|
for n in 0..<16 {
|
|
to[n] = from[n]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
gprs.valid = 0xffff
|
|
}
|
|
|
|
public static func fromHostMContext(_ mcontext: Any) -> HostContext {
|
|
return ARMContext(with: mcontext as! mcontext_t)
|
|
}
|
|
#endif
|
|
|
|
#if os(Windows) || !SWIFT_ASM_AVAILABLE
|
|
struct NotImplemented: Error {}
|
|
public static func withCurrentContext<T>(fn: (ARMContext) throws -> T) throws -> T {
|
|
throw NotImplemented()
|
|
}
|
|
#elseif arch(arm)
|
|
@usableFromInline
|
|
@_silgen_name("_swift_get_cpu_context")
|
|
static func _swift_get_cpu_context() -> ARMContext
|
|
|
|
@_transparent
|
|
public static func withCurrentContext<T>(fn: (ARMContext) throws -> T) rethrows -> T {
|
|
return try fn(_swift_get_cpu_context())
|
|
}
|
|
#endif
|
|
|
|
private func isValid(_ register: Register) -> Bool {
|
|
if register.rawValue < 16 {
|
|
return (gprs.valid & (UInt32(1) << register.rawValue)) != 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
private mutating func setValid(_ register: Register) {
|
|
if register.rawValue < 16 {
|
|
gprs.valid |= UInt32(1) << register.rawValue
|
|
}
|
|
}
|
|
|
|
private mutating func clearValid(_ register: Register) {
|
|
if register.rawValue < 16 {
|
|
gprs.valid &= ~(UInt32(1) << register.rawValue)
|
|
}
|
|
}
|
|
|
|
public func getRegister(_ reg: Register) -> GPRValue? {
|
|
if !isValid(reg) {
|
|
return nil
|
|
}
|
|
switch reg {
|
|
case .r0 ... .r15:
|
|
return gprs.getR(reg.rawValue)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public mutating func setRegister(_ reg: Register, to value: GPRValue?) {
|
|
if let value = value {
|
|
switch reg {
|
|
case .r0 ... .r15:
|
|
gprs.setR(reg.rawValue, to: value)
|
|
default:
|
|
break
|
|
}
|
|
} else {
|
|
clearValid(reg)
|
|
}
|
|
}
|
|
|
|
public var description: String {
|
|
return """
|
|
r0: \(hex(gprs.getR(0))) r1: \(hex(gprs.getR(1)))
|
|
r2: \(hex(gprs.getR(2))) r3: \(hex(gprs.getR(3)))
|
|
r4: \(hex(gprs.getR(4))) r5: \(hex(gprs.getR(5)))
|
|
r6: \(hex(gprs.getR(6))) r7: \(hex(gprs.getR(7)))
|
|
r8: \(hex(gprs.getR(8))) r9: \(hex(gprs.getR(9)))
|
|
r10: \(hex(gprs.getR(10)))
|
|
|
|
fp: \(hex(gprs.getR(11))) (aka r11)
|
|
ip: \(hex(gprs.getR(12))) (aka r12)
|
|
sp: \(hex(gprs.getR(13))) (aka r13)
|
|
lr: \(hex(gprs.getR(14))) (aka r14)
|
|
pc: \(hex(gprs.getR(15))) (aka r15)
|
|
"""
|
|
}
|
|
|
|
public static func isAlignedForStack(framePointer: Address) -> Bool {
|
|
return (framePointer & 1) == 0
|
|
}
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
internal static var coreSymbolicationArchitecture: CSArchitecture {
|
|
return kCSArchitectureArmV7K
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// .. Darwin specifics .........................................................
|
|
|
|
#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS))
|
|
private func thread_get_state<T>(_ thread: thread_t,
|
|
_ flavor: CInt,
|
|
_ result: inout T) -> kern_return_t {
|
|
var count: mach_msg_type_number_t
|
|
= mach_msg_type_number_t(MemoryLayout<T>.stride
|
|
/ MemoryLayout<natural_t>.stride)
|
|
|
|
return withUnsafeMutablePointer(to: &result) { ptr in
|
|
ptr.withMemoryRebound(to: natural_t.self,
|
|
capacity: Int(count)) { intPtr in
|
|
return thread_get_state(thread,
|
|
thread_state_flavor_t(flavor),
|
|
intPtr,
|
|
&count)
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// .. HostContext ..............................................................
|
|
|
|
/// HostContext is an alias for the appropriate context for the machine on which
|
|
/// the code was compiled.
|
|
#if arch(x86_64)
|
|
@_spi(Contexts) public typealias HostContext = X86_64Context
|
|
#elseif arch(i386)
|
|
@_spi(Contexts) public typealias HostContext = I386Context
|
|
#elseif arch(arm64) || arch(arm64_32)
|
|
@_spi(Contexts) public typealias HostContext = ARM64Context
|
|
#elseif arch(arm)
|
|
@_spi(Contexts) public typealias HostContext = ARMContext
|
|
#endif
|