//===--- SwiftReflectionTest.swift ----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // //===----------------------------------------------------------------------===// // // This file provides infrastructure for introspecting type information in a // remote swift executable by swift-reflection-test, using pipes and a // request-response protocol to communicate with the test tool. // //===----------------------------------------------------------------------===// let RequestInstanceKind = "k" let RequestInstanceAddress = "i" let RequestReflectionInfos = "r" let RequestImages = "m" let RequestReadBytes = "b" let RequestSymbolAddress = "s" let RequestStringLength = "l" let RequestDone = "d" let RequestPointerSize = "p" #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import MachO import Darwin #if arch(x86_64) || arch(arm64) typealias MachHeader = mach_header_64 #else typealias MachHeader = mach_header #endif /// Get the location and size of a section in a binary. /// /// - Parameter name: The name of the section /// - Parameter imageHeader: A pointer to the Mach header describing the /// image. /// - Returns: A `Section` containing the address and size, or `nil` if there /// is no section by the given name. internal func getSectionInfo(_ name: String, _ imageHeader: UnsafePointer) -> Section? { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } var size: UInt = 0 let address = getsectiondata(imageHeader, "__TEXT", name, &size) guard let nonNullAddress = address else { return nil } guard size != 0 else { return nil } return Section(startAddress: nonNullAddress, size: size) } /// Get the TEXT segment location and size for a loaded image. /// /// - Parameter i: The index of the loaded image as reported by Dyld. /// - Returns: The image name, address, and size. internal func getAddressInfoForImage(atIndex i: UInt32) -> (name: String, address: UnsafeMutablePointer?, size: UInt) { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let header = unsafeBitCast(_dyld_get_image_header(i), to: UnsafePointer.self) let name = String(validatingUTF8: _dyld_get_image_name(i)!)! var size: UInt = 0 let address = getsegmentdata(header, "__TEXT", &size) return (name, address, size) } /// Send all loadedimages loaded in the current process. internal func sendImages() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let infos = (0..= 1) sendValue(infos.count) for (name, address, size) in infos { debugLog("Sending info for \(name)") sendValue(address) sendValue(size) } } /// Get the Swift Reflection section locations for a loaded image. /// /// An image of interest must have the following sections in the __TEXT /// segment: /// - __swift5_fieldmd /// - __swift5_assocty /// - __swift5_builtin /// - __swift5_capture /// - __swift5_typeref /// - __swift5_reflstr (optional, may have been stripped out) /// /// - Parameter i: The index of the loaded image as reported by Dyld. /// - Returns: A `ReflectionInfo` containing the locations of all of the /// needed sections, or `nil` if the image doesn't contain all of them. internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let header = unsafeBitCast(_dyld_get_image_header(i), to: UnsafePointer.self) let imageName = _dyld_get_image_name(i)! let fieldmd = getSectionInfo("__swift5_fieldmd", header) let assocty = getSectionInfo("__swift5_assocty", header) let builtin = getSectionInfo("__swift5_builtin", header) let capture = getSectionInfo("__swift5_capture", header) let typeref = getSectionInfo("__swift5_typeref", header) let reflstr = getSectionInfo("__swift5_reflstr", header) return ReflectionInfo(imageName: String(validatingUTF8: imageName)!, fieldmd: fieldmd, assocty: assocty, builtin: builtin, capture: capture, typeref: typeref, reflstr: reflstr) } internal func getImageCount() -> UInt32 { return _dyld_image_count() } let rtldDefault = UnsafeMutableRawPointer(bitPattern: Int(-2)) #elseif !os(Windows) import SwiftShims import Glibc let rtldDefault: UnsafeMutableRawPointer? = nil extension Section { init(range: MetadataSectionRange) { self.startAddress = UnsafeRawPointer(bitPattern: range.start)! self.size = UInt(range.length) } } internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? { return _getMetadataSection(UInt(i)).map { rawPointer in let name = _getMetadataSectionName(rawPointer) let metadataSection = rawPointer.bindMemory(to: MetadataSections.self, capacity: 1).pointee return ReflectionInfo(imageName: String(validatingUTF8: name)!, fieldmd: Section(range: metadataSection.swift5_fieldmd), assocty: Section(range: metadataSection.swift5_assocty), builtin: Section(range: metadataSection.swift5_builtin), capture: Section(range: metadataSection.swift5_capture), typeref: Section(range: metadataSection.swift5_typeref), reflstr: Section(range: metadataSection.swift5_reflstr)) } } internal func getImageCount() -> UInt32 { return UInt32(_getMetadataSectionCount()) } internal func sendImages() { preconditionFailure("Should only be called in macOS!") } #else // os(Linux) #error("SwiftReflectionTest does not currently support this OS.") #endif internal func debugLog(_ message: @autoclosure () -> String) { #if DEBUG_LOG fputs("Child: \(message())\n", stderr) fflush(stderr) #endif } public enum InstanceKind: UInt8 { case None case Object case Existential case ErrorExistential case Closure case Enum case EnumValue } /// Represents a section in a loaded image in this process. internal struct Section { /// The absolute start address of the section's data in this address space. let startAddress: UnsafeRawPointer /// The size of the section in bytes. let size: UInt } /// Holds the addresses and sizes of sections related to reflection. internal struct ReflectionInfo : Sequence { /// The name of the loaded image. internal let imageName: String /// Reflection metadata sections. internal let fieldmd: Section? internal let assocty: Section? internal let builtin: Section? internal let capture: Section? internal let typeref: Section? internal let reflstr: Section? internal func makeIterator() -> AnyIterator { return AnyIterator([ fieldmd, assocty, builtin, capture, typeref, reflstr ].makeIterator()) } } internal func sendBytes(from address: UnsafePointer, count: Int) { var source = UnsafeRawPointer(address) var bytesLeft = count debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } while bytesLeft > 0 { let bytesWritten = fwrite(source, 1, bytesLeft, stdout) fflush(stdout) guard bytesWritten > 0 else { fatalError("Couldn't write to parent pipe") } bytesLeft -= bytesWritten source = source.advanced(by: bytesWritten) } } /// Send the address of an object to the parent. internal func sendAddress(of instance: AnyObject) { debugLog("BEGIN \(#function)") defer { debugLog("END \(#function)") } var address = Unmanaged.passUnretained(instance).toOpaque() sendBytes(from: &address, count: MemoryLayout.size) } /// Send the `value`'s bits to the parent. internal func sendValue(_ value: T) { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } var value = value sendBytes(from: &value, count: MemoryLayout.size) } /// Read a word-sized unsigned integer from the parent. internal func readUInt() -> UInt { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } var value: UInt = 0 fread(&value, MemoryLayout.size, 1, stdin) return value } /// Send all known `ReflectionInfo`s for all images loaded in the current /// process. internal func sendReflectionInfos() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let infos = (0..= 1) sendBytes(from: &numInfos, count: MemoryLayout.size) for info in infos { debugLog("Sending info for \(info.imageName)") for section in info { sendValue(section?.startAddress) sendValue(section?.size ?? 0) } } } internal func printErrnoAndExit() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let errorCString = strerror(errno)! let message = String(validatingUTF8: errorCString)! + "\n" let bytes = Array(message.utf8) fwrite(bytes, 1, bytes.count, stderr) fflush(stderr) exit(EXIT_FAILURE) } /// Retrieve the address and count from the parent and send the bytes back. internal func sendBytes() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let address = readUInt() let count = Int(readUInt()) debugLog("Parent requested \(count) bytes from \(address)") var totalBytesWritten = 0 var pointer = UnsafeMutableRawPointer(bitPattern: address) while totalBytesWritten < count { let bytesWritten = Int(fwrite(pointer, 1, Int(count), stdout)) fflush(stdout) if bytesWritten == 0 { printErrnoAndExit() } totalBytesWritten += bytesWritten pointer = pointer?.advanced(by: bytesWritten) } } /// Send the address of a symbol loaded in this process. internal func sendSymbolAddress() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let name = readLine()! name.withCString { let symbol = dlsym(rtldDefault, $0) let symbolAddress = unsafeBitCast(symbol, to: UInt.self) sendValue(symbolAddress) } } /// Send the length of a string to the parent. internal func sendStringLength() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let address = readUInt() let cString = UnsafePointer(bitPattern: address)! var count = 0 while cString[count] != CChar(0) { count = count + 1 } sendValue(count) } /// Send the size of this architecture's pointer type. internal func sendPointerSize() { debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") } let pointerSize = UInt8(MemoryLayout.size) sendValue(pointerSize) } /// Hold an `instance` and wait for the parent to query for information. /// /// This is the main "run loop" of the test harness. /// /// The parent will necessarily need to: /// - Get the addresses of any swift dylibs that are loaded, where applicable. /// - Get the address of the `instance` /// - Get the pointer size of this process, which affects assumptions about the /// the layout of runtime structures with pointer-sized fields. /// - Read raw bytes out of this process's address space. /// /// The parent sends a Done message to indicate that it's done /// looking at this instance. It will continue to ask for instances, /// so call doneReflecting() when you don't have any more instances. internal func reflect(instanceAddress: UInt, kind: InstanceKind) { while let command = readLine(strippingNewline: true) { switch command { case String(validatingUTF8: RequestInstanceKind)!: sendValue(kind.rawValue) case String(validatingUTF8: RequestInstanceAddress)!: sendValue(instanceAddress) case String(validatingUTF8: RequestReflectionInfos)!: sendReflectionInfos() case String(validatingUTF8: RequestImages)!: sendImages() case String(validatingUTF8: RequestReadBytes)!: sendBytes() case String(validatingUTF8: RequestSymbolAddress)!: sendSymbolAddress() case String(validatingUTF8: RequestStringLength)!: sendStringLength() case String(validatingUTF8: RequestPointerSize)!: sendPointerSize() case String(validatingUTF8: RequestDone)!: return default: fatalError("Unknown request received: '\(Array(command.utf8))'!") } } } /// Reflect a class instance. /// /// This reflects the stored properties of the immediate class. /// The superclass is not (yet?) visited. public func reflect(object: AnyObject) { defer { _fixLifetime(object) } let address = Unmanaged.passUnretained(object).toOpaque() let addressValue = UInt(bitPattern: address) reflect(instanceAddress: addressValue, kind: .Object) } /// Reflect any type at all by boxing it into an existential container (an `Any`) /// /// Given a class, this will reflect the reference value, and not the contents /// of the instance. Use `reflect(object:)` for that. /// /// This function serves to exercise the projectExistential function of the /// SwiftRemoteMirror API. /// /// It tests the three conditions of existential layout: /// /// ## Class existentials /// /// For example, a `MyClass as Any`: /// ``` /// [Pointer to class instance] /// [Witness table 1] /// [Witness table 2] /// ... /// [Witness table n] /// ``` /// /// ## Existentials whose contained type fits in the 3-word buffer /// /// For example, a `(1, 2) as Any`: /// ``` /// [Tuple element 1: Int] /// [Tuple element 2: Int] /// [-Empty_] /// [Metadata Pointer] /// [Witness table 1] /// [Witness table 2] /// ... /// [Witness table n] /// ``` /// /// ## Existentials whose contained type has to be allocated into a /// heap buffer. /// /// For example, a `LargeStruct as Any`: /// ``` /// [Pointer to unmanaged heap container] --> [Large struct] /// [-Empty-] /// [-Empty-] /// [Metadata Pointer] /// [Witness table 1] /// [Witness table 2] /// ... /// [Witness table n] /// ``` /// /// The test doesn't care about the witness tables - we only care /// about what's in the buffer, so we always put these values into /// an Any existential. public func reflect(any: T, kind: InstanceKind = .Existential) { let any: Any = any let anyPointer = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size) anyPointer.initialize(to: any) let anyPointerValue = UInt(bitPattern: anyPointer) reflect(instanceAddress: anyPointerValue, kind: kind) anyPointer.deallocate() } // Reflect an `Error`, a.k.a. an "error existential". // // These are always boxed on the heap, with the following layout: // // - Word 0: Metadata Pointer // - Word 1: 2x 32-bit reference counts // // If Objective-C interop is available, an Error is also an // `NSError`, and so has: // // - Word 2: code (NSInteger) // - Word 3: domain (NSString *) // - Word 4: userInfo (NSDictionary *) // // Then, always follow: // // - Word 2 or 5: Instance type metadata pointer // - Word 3 or 6: Instance witness table for conforming // to `Swift.Error`. // // Following that is the instance that conforms to `Error`, // rounding up to its alignment. public func reflect(error: T) { let error: Error = error let errorPointerValue = unsafeBitCast(error, to: UInt.self) reflect(instanceAddress: errorPointerValue, kind: .ErrorExistential) } // Reflect an `Enum` // // These are handled like existentials, but // the test driver verifies a different set of data. public func reflect(enum value: T) { reflect(any: value, kind: .Enum) } public func reflect(enumValue value: T) { reflect(any: value, kind: .EnumValue) } /// Wraps a thick function with arity 0. struct ThickFunction0 { var function: () -> Void } /// Wraps a thick function with arity 1. struct ThickFunction1 { var function: (Int) -> Void } /// Wraps a thick function with arity 2. struct ThickFunction2 { var function: (Int, String) -> Void } /// Wraps a thick function with arity 3. struct ThickFunction3 { var function: (Int, String, AnyObject?) -> Void } struct ThickFunctionParts { var function: UnsafeRawPointer var context: Optional } /// Reflect a closure context. The given function must be a Swift-native /// @convention(thick) function value. public func reflect(function: @escaping () -> Void) { let fn = UnsafeMutablePointer.allocate( capacity: MemoryLayout.size) fn.initialize(to: ThickFunction0(function: function)) let contextPointer = fn.withMemoryRebound( to: ThickFunctionParts.self, capacity: 1) { UInt(bitPattern: $0.pointee.context) } reflect(instanceAddress: contextPointer, kind: .Object) fn.deallocate() } /// Reflect a closure context. The given function must be a Swift-native /// @convention(thick) function value. public func reflect(function: @escaping (Int) -> Void) { let fn = UnsafeMutablePointer.allocate( capacity: MemoryLayout.size) fn.initialize(to: ThickFunction1(function: function)) let contextPointer = fn.withMemoryRebound( to: ThickFunctionParts.self, capacity: 1) { UInt(bitPattern: $0.pointee.context) } reflect(instanceAddress: contextPointer, kind: .Object) fn.deallocate() } /// Reflect a closure context. The given function must be a Swift-native /// @convention(thick) function value. public func reflect(function: @escaping (Int, String) -> Void) { let fn = UnsafeMutablePointer.allocate( capacity: MemoryLayout.size) fn.initialize(to: ThickFunction2(function: function)) let contextPointer = fn.withMemoryRebound( to: ThickFunctionParts.self, capacity: 1) { UInt(bitPattern: $0.pointee.context) } reflect(instanceAddress: contextPointer, kind: .Object) fn.deallocate() } /// Reflect a closure context. The given function must be a Swift-native /// @convention(thick) function value. public func reflect(function: @escaping (Int, String, AnyObject?) -> Void) { let fn = UnsafeMutablePointer.allocate( capacity: MemoryLayout.size) fn.initialize(to: ThickFunction3(function: function)) let contextPointer = fn.withMemoryRebound( to: ThickFunctionParts.self, capacity: 1) { UInt(bitPattern: $0.pointee.context) } reflect(instanceAddress: contextPointer, kind: .Object) fn.deallocate() } /// Call this function to indicate to the parent that there are /// no more instances to look at. public func doneReflecting() { reflect(instanceAddress: UInt(InstanceKind.None.rawValue), kind: .None) } /* Example usage public protocol P { associatedtype Index var startIndex: Index { get } } public struct Thing : P { public let startIndex = 1010 } public enum E { case B(T) case C(T.Index) } public class A : P { public let x: T? public let y: T.Index public let b = true public let t = (1, 1.0) private let type: NSObject.Type public let startIndex = 1010 public init(x: T) { self.x = x self.y = x.startIndex self.type = NSObject.self } } let instance = A(x: A(x: Thing())) reflect(A(x: Thing())) */