Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ class MetadataReader {
if (!meta || meta->getKind() != MetadataKind::Class)
return None;

#if SWIFT_OBJC_INTEROP
// The following algorithm only works on the non-fragile Apple runtime.

// Grab the RO-data pointer. This part is not ABI.
Expand All @@ -524,6 +525,23 @@ class MetadataReader {
return None;

return start;
#else
// All swift class instances start with an isa pointer,
// followed by the retain counts (which are the size of a long long).
size_t isaAndRetainCountSize = sizeof(StoredSize) + sizeof(long long);
size_t start = isaAndRetainCountSize;

auto classMeta = cast<TargetClassMetadata<Runtime>>(meta);
while (classMeta->Superclass) {
classMeta = cast<TargetClassMetadata<Runtime>>(
readMetadata(classMeta->Superclass));

// Subtract the size contribution of the isa and retain counts from
// the super class.
start += classMeta->InstanceSize - isaAndRetainCountSize;
}
return start;
#endif
}

/// Given a pointer to the metadata, attempt to read the value
Expand Down
5 changes: 5 additions & 0 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ void
swift_reflection_addReflectionInfo(SwiftReflectionContextRef ContextRef,
swift_reflection_info_t Info);

/// Add reflection sections from a loaded Swift image.
SWIFT_REMOTE_MIRROR_LINKAGE
void swift_reflection_addReflectionMappingInfo(
SwiftReflectionContextRef ContextRef, swift_reflection_mapping_info_t Info);

/// Add reflection information from a loaded Swift image.
/// Returns true on success, false if the image's memory couldn't be read.
SWIFT_REMOTE_MIRROR_LINKAGE
Expand Down
22 changes: 22 additions & 0 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,23 @@ typedef struct swift_reflection_section {
void *End;
} swift_reflection_section_t;

/// Represents the remote address and size of an image's section
typedef struct swift_remote_reflection_section {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually a section as per the file format or is this really just the specific metadata table?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supposed to represent the counterpart of the already existing swift_reflection_section struct. That's why I named it swift_remote_reflection_section. Is this name wrong? Aren't associated_types, builtin_types, capture, etc sections?

Copy link
Member

@compnerd compnerd Jul 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are sections only on ELF because of the restrictions from the file format. Each one of those is a separate metadata table. In fact, if you look at MachO, they are all in the __DATA section and each is a segment. PE/COFF does something similar - entries are grouped, and then emitted entirely into the .data section. If this is meant to be ELF specific, please put that into the name (e.g. swift_remote_reflection_elf_section).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have that backwards, __DATA is the segment (they're actually in __TEXT, though, because they're constant), and __TEXT, __swift5_foo is the section, in Mach-O parlance. The name seems correct to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay, if that is constant across all the formats, sounds reasonable.

uintptr_t StartAddress;
uintptr_t Size;
} swift_remote_reflection_section_t;

typedef struct swift_reflection_section_pair {
swift_reflection_section_t section;
swift_reflection_ptr_t offset; ///< DEPRECATED. Must be zero
} swift_reflection_section_pair_t;

/// Represents the mapping between an image sections's local and remote addresses
typedef struct swift_reflection_section_mapping {
swift_reflection_section_t local_section;
swift_remote_reflection_section_t remote_section;
} swift_reflection_section_mapping_t;

/// Represents the set of Swift reflection sections of an image.
/// Not all sections may be present.
///
Expand All @@ -71,6 +83,16 @@ typedef struct swift_reflection_info {
swift_reflection_ptr_t RemoteStartAddress;
} swift_reflection_info_t;

/// Represents the set of Swift reflection sections of an image,
typedef struct swift_reflection_mapping_info {
swift_reflection_section_mapping_t field;
swift_reflection_section_mapping_t associated_types;
swift_reflection_section_mapping_t builtin_types;
swift_reflection_section_mapping_t capture;
swift_reflection_section_mapping_t type_references;
swift_reflection_section_mapping_t reflection_strings;
} swift_reflection_mapping_info_t;

/// The layout kind of a Swift type.
typedef enum swift_layout_kind {
// Nothing is known about the size or contents of this value.
Expand Down
8 changes: 5 additions & 3 deletions stdlib/private/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ if(SWIFT_BUILD_SDK_OVERLAY)

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_subdirectory(StdlibUnittestFoundationExtras)
if (SWIFT_INCLUDE_TESTS)
add_subdirectory(SwiftReflectionTest)
endif()
endif()
# Currently SwiftReflectionTest cannot be built on Windows, due to
# dependencies on POSIX symbols
if (SWIFT_INCLUDE_TESTS AND (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows"))
add_subdirectory(SwiftReflectionTest)
endif()
endif()
214 changes: 129 additions & 85 deletions stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@
//
//===----------------------------------------------------------------------===//

// FIXME: Make this work with Linux

import MachO
import Darwin

let RequestInstanceKind = "k"
let RequestInstanceAddress = "i"
let RequestReflectionInfos = "r"
Expand All @@ -31,56 +26,10 @@ let RequestStringLength = "l"
let RequestDone = "d"
let RequestPointerSize = "p"

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<Section?> {
return AnyIterator([
fieldmd,
assocty,
builtin,
capture,
typeref,
reflstr
].makeIterator())
}
}
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import MachO
import Darwin

#if arch(x86_64) || arch(arm64)
typealias MachHeader = mach_header_64
Expand All @@ -105,6 +54,36 @@ internal func getSectionInfo(_ name: String,
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<UInt8>?, size: UInt) {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let header = unsafeBitCast(_dyld_get_image_header(i),
to: UnsafePointer<MachHeader>.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..<getImageCount()).map(getAddressInfoForImage)

debugLog("\(infos.count) reflection info bundles.")
precondition(infos.count >= 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
Expand Down Expand Up @@ -140,19 +119,100 @@ internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? {
reflstr: reflstr)
}

/// 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<UInt8>?, size: UInt) {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let header = unsafeBitCast(_dyld_get_image_header(i),
to: UnsafePointer<MachHeader>.self)
let name = String(validatingUTF8: _dyld_get_image_name(i)!)!
var size: UInt = 0
let address = getsegmentdata(header, "__TEXT", &size)
return (name, address, size)
internal func getImageCount() -> UInt32 {
return _dyld_image_count()
}

let rtldDefault = UnsafeMutableRawPointer(bitPattern: Int(-2))
#elseif !os(Windows)
import SwiftShims
import Glibc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about if we are on windows. @compnerd

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, I am fine if we just have an assert here or something like that so if someone tries to use it, we get an assertion or something. Just an else doesn't mean linux is the point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows and Linux paths should be near identical. The way that I had setup the reflection information on both of those platforms, each image has a structure with the addresses that is rebased by the loader. At runtime, the information is registered with the runtime (see the SwiftImageRegistrar). The section should have all the information that is needed. I think that supporting Windows with the same path should be a very simple and straightforward thing to do and should be done as part of this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@compnerd does Glibc exist on Windows? If it doesn't is there some analogous module that defines stdin, stdout, errno, etc I can import instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are examples of how to do portable file I/O in stdlib/private/StdlibUnittest/StdlibUnittest.swift and tdlib/private/SwiftPrivateLibcExtras/Subprocess.swift, and stdlib/public/Platform/Platform.swift. Is it possible to just import one of those?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errno is largely unhelpful on Windows, the API matters significantly for the error cases. stdin, stdout, and stderr are available in MSVCRT.


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<Section?> {
return AnyIterator([
fieldmd,
assocty,
builtin,
capture,
typeref,
reflstr
].makeIterator())
}
}

internal func sendBytes<T>(from address: UnsafePointer<T>, count: Int) {
Expand Down Expand Up @@ -197,7 +257,7 @@ internal func readUInt() -> UInt {
/// process.
internal func sendReflectionInfos() {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let infos = (0..<_dyld_image_count()).compactMap(getReflectionInfoForImage)
let infos = (0..<getImageCount()).compactMap(getReflectionInfoForImage)

var numInfos = infos.count
debugLog("\(numInfos) reflection info bundles.")
Expand All @@ -212,21 +272,6 @@ internal func sendReflectionInfos() {
}
}

/// Send all loadedimages loaded in the current process.
internal func sendImages() {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let infos = (0..<_dyld_image_count()).map(getAddressInfoForImage)

debugLog("\(infos.count) reflection info bundles.")
precondition(infos.count >= 1)
sendValue(infos.count)
for (name, address, size) in infos {
debugLog("Sending info for \(name)")
sendValue(address)
sendValue(size)
}
}

internal func printErrnoAndExit() {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let errorCString = strerror(errno)!
Expand Down Expand Up @@ -261,8 +306,7 @@ internal func sendSymbolAddress() {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let name = readLine()!
name.withCString {
let handle = UnsafeMutableRawPointer(bitPattern: Int(-2))!
let symbol = dlsym(handle, $0)
let symbol = dlsym(rtldDefault, $0)
let symbolAddress = unsafeBitCast(symbol, to: UInt.self)
sendValue(symbolAddress)
}
Expand Down
Loading