mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
803 lines
26 KiB
C
803 lines
26 KiB
C
//===--- swift-reflection-test.c - Reflection testing application ---------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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 supports performing target-specific remote reflection tests
|
|
// on live swift executables.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
#define SECTIONS_PER_INFO 6
|
|
|
|
#include "swift/SwiftRemoteMirror/SwiftRemoteMirror.h"
|
|
#include "swift/Demangling/ManglingMacros.h"
|
|
#include "messages.h"
|
|
#include "overrides.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
#include <unistd.h>
|
|
#elif defined(_WIN32)
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#include <TargetConditionals.h>
|
|
#endif
|
|
|
|
#if __has_feature(ptrauth_calls)
|
|
#include <ptrauth.h>
|
|
#endif
|
|
|
|
#if defined(__clang__) || defined(__GNUC__)
|
|
#define NORETURN __attribute__((noreturn))
|
|
#elif defined(_MSC_VER)
|
|
#define NORETURN __declspec(noreturn)
|
|
#else
|
|
#define NORETURN
|
|
#endif
|
|
|
|
typedef struct PipeMemoryReader {
|
|
int to_child[2];
|
|
int from_child[2];
|
|
} PipeMemoryReader;
|
|
|
|
|
|
NORETURN
|
|
static void errorAndExit(const char *message) {
|
|
fprintf(stderr, "%s\n", message);
|
|
abort();
|
|
}
|
|
|
|
NORETURN
|
|
static void errnoAndExit(const char *message) {
|
|
fprintf(stderr, "%s: %s\n", message, strerror(errno));
|
|
abort();
|
|
}
|
|
|
|
#if 0
|
|
#define DEBUG_LOG(fmt, ...) fprintf(stderr, "%s: " fmt "\n",\
|
|
__func__, __VA_ARGS__)
|
|
#else
|
|
#define DEBUG_LOG(fmt, ...) (void)0
|
|
#endif
|
|
|
|
static const size_t ReadEnd = 0;
|
|
static const size_t WriteEnd = 1;
|
|
|
|
static
|
|
int PipeMemoryReader_getParentReadFD(const PipeMemoryReader *Reader) {
|
|
return Reader->from_child[ReadEnd];
|
|
}
|
|
|
|
static
|
|
int PipeMemoryReader_getChildWriteFD(const PipeMemoryReader *Reader) {
|
|
return Reader->from_child[WriteEnd];
|
|
}
|
|
|
|
static
|
|
int PipeMemoryReader_getParentWriteFD(const PipeMemoryReader *Reader) {
|
|
return Reader->to_child[WriteEnd];
|
|
}
|
|
|
|
static
|
|
int PipeMemoryReader_getChildReadFD(const PipeMemoryReader *Reader) {
|
|
return Reader->to_child[ReadEnd];
|
|
}
|
|
|
|
static
|
|
uint8_t PipeMemoryReader_getPointerSize(void *Context) {
|
|
return sizeof(uintptr_t);
|
|
}
|
|
|
|
static
|
|
void PipeMemoryReader_collectBytesFromPipe(const PipeMemoryReader *Reader,
|
|
void *Dest, size_t Size) {
|
|
int ReadFD = PipeMemoryReader_getParentReadFD(Reader);
|
|
while (Size) {
|
|
int bytesRead = read(ReadFD, Dest, Size);
|
|
if (bytesRead < 0)
|
|
if (errno == EINTR)
|
|
continue;
|
|
else
|
|
errnoAndExit("collectBytesFromPipe");
|
|
else if (bytesRead == 0)
|
|
errorAndExit("collectBytesFromPipe: Unexpected end of file");
|
|
Size -= bytesRead;
|
|
// Arithmetic on a void pointer is a GNU extension.
|
|
Dest = (char*)(Dest) + bytesRead;
|
|
}
|
|
}
|
|
|
|
static int PipeMemoryReader_queryDataLayout(void *Context,
|
|
DataLayoutQueryType type,
|
|
void *inBuffer, void *outBuffer) {
|
|
#if defined(__APPLE__) && __APPLE__
|
|
int applePlatform = 1;
|
|
#else
|
|
int applePlatform = 0;
|
|
#endif
|
|
#if defined(__APPLE__) && __APPLE__ && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_IOS) && TARGET_OS_WATCH) || (defined(TARGET_OS_TV) && TARGET_OS_TV) || defined(__arm64__))
|
|
int iosDerivedPlatform = 1;
|
|
#else
|
|
int iosDerivedPlatform = 0;
|
|
#endif
|
|
|
|
switch (type) {
|
|
case DLQ_GetPointerSize: {
|
|
uint8_t *result = (uint8_t *)outBuffer;
|
|
*result = sizeof(void *);
|
|
return 1;
|
|
}
|
|
case DLQ_GetSizeSize: {
|
|
uint8_t *result = (uint8_t *)outBuffer;
|
|
*result = sizeof(size_t);
|
|
return 1;
|
|
}
|
|
case DLQ_GetPtrAuthMask: {
|
|
uintptr_t *result = (uintptr_t *)outBuffer;
|
|
#if __has_feature(ptrauth_calls)
|
|
*result = (uintptr_t)ptrauth_strip((void*)0x0007ffffffffffff, 0);
|
|
#else
|
|
*result = (uintptr_t)~0ull;
|
|
#endif
|
|
return 1;
|
|
}
|
|
case DLQ_GetObjCReservedLowBits: {
|
|
uint8_t *result = (uint8_t *)outBuffer;
|
|
if (applePlatform && !iosDerivedPlatform && (sizeof(void *) == 8)) {
|
|
// Only for 64-bit macOS (not iOS, not even when simulated on x86_64)
|
|
*result = 1;
|
|
} else {
|
|
*result = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
case DLQ_GetLeastValidPointerValue: {
|
|
uint64_t *result = (uint64_t *)outBuffer;
|
|
if (applePlatform && (sizeof(void *) == 8)) {
|
|
// Swift reserves the first 4GiB on Apple 64-bit platforms
|
|
*result = 0x100000000;
|
|
return 1;
|
|
} else {
|
|
// Swift reserves the first 4KiB everywhere else
|
|
*result = 0x1000;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void PipeMemoryReader_freeBytes(void *reader_context, const void *bytes,
|
|
void *context) {
|
|
free((void *)bytes);
|
|
}
|
|
|
|
static
|
|
const void *PipeMemoryReader_readBytes(void *Context, swift_addr_t Address,
|
|
uint64_t Size, void **outFreeContext) {
|
|
const PipeMemoryReader *Reader = (const PipeMemoryReader *)Context;
|
|
uintptr_t TargetAddress = Address;
|
|
size_t TargetSize = (size_t)Size;
|
|
DEBUG_LOG("Requesting read of %zu bytes from 0x%" PRIxPTR,
|
|
TargetSize, TargetAddress);
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_READ_BYTES, 2);
|
|
write(WriteFD, &TargetAddress, sizeof(TargetAddress));
|
|
write(WriteFD, &TargetSize, sizeof(size_t));
|
|
|
|
void *Buf = malloc(Size);
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, Buf, Size);
|
|
|
|
*outFreeContext = NULL;
|
|
|
|
return Buf;
|
|
}
|
|
|
|
static
|
|
swift_addr_t PipeMemoryReader_getSymbolAddress(void *Context,
|
|
const char *SymbolName,
|
|
uint64_t Length) {
|
|
const PipeMemoryReader *Reader = (const PipeMemoryReader *)Context;
|
|
uintptr_t Address = 0;
|
|
DEBUG_LOG("Requesting address of symbol %s", SymbolName);
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_SYMBOL_ADDRESS, 2);
|
|
write(WriteFD, SymbolName, Length);
|
|
write(WriteFD, "\n", 1);
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, (uint8_t*)&Address,
|
|
sizeof(Address));
|
|
DEBUG_LOG("Address of %s is 0x%" PRIxPTR, SymbolName, Address);
|
|
return (uintptr_t)Address;
|
|
}
|
|
|
|
static InstanceKind
|
|
PipeMemoryReader_receiveInstanceKind(const PipeMemoryReader *Reader) {
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_INSTANCE_KIND, 2);
|
|
uint8_t KindValue = 0;
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, &KindValue, sizeof(KindValue));
|
|
DEBUG_LOG("Requested instance kind is %u", KindValue);
|
|
return KindValue;
|
|
}
|
|
|
|
static uintptr_t
|
|
PipeMemoryReader_receiveInstanceAddress(const PipeMemoryReader *Reader) {
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_INSTANCE_ADDRESS, 2);
|
|
uintptr_t InstanceAddress = 0;
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, (uint8_t *)&InstanceAddress,
|
|
sizeof(InstanceAddress));
|
|
DEBUG_LOG("Requested instance address is 0x%" PRIxPTR, InstanceAddress);
|
|
return InstanceAddress;
|
|
}
|
|
|
|
static
|
|
void PipeMemoryReader_sendDoneMessage(const PipeMemoryReader *Reader) {
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_DONE, 2);
|
|
}
|
|
|
|
static
|
|
PipeMemoryReader createPipeMemoryReader() {
|
|
PipeMemoryReader Reader;
|
|
#if defined(_WIN32)
|
|
if (_pipe(Reader.to_child, 256, _O_BINARY))
|
|
errnoAndExit("Couldn't create pipes to child process");
|
|
if (_pipe(Reader.from_child, 256, _O_BINARY))
|
|
errnoAndExit("Couldn't create pipes from child process");
|
|
#else
|
|
if (pipe(Reader.to_child))
|
|
errnoAndExit("Couldn't create pipes to child process");
|
|
if (pipe(Reader.from_child))
|
|
errnoAndExit("Couldn't create pipes from child process");
|
|
#endif
|
|
return Reader;
|
|
}
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
static void
|
|
PipeMemoryReader_receiveImages(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader *Reader) {
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_IMAGES, 2);
|
|
size_t NumReflectionInfos;
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, &NumReflectionInfos,
|
|
sizeof(NumReflectionInfos));
|
|
DEBUG_LOG("Receiving %zu images from child", NumReflectionInfos);
|
|
|
|
if (NumReflectionInfos == 0)
|
|
return;
|
|
|
|
struct { uintptr_t Start, Size; } *Images;
|
|
Images = calloc(NumReflectionInfos, sizeof(*Images));
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, Images,
|
|
NumReflectionInfos * sizeof(*Images));
|
|
|
|
for (size_t i = 0; i < NumReflectionInfos; ++i) {
|
|
DEBUG_LOG("Adding image at 0x%" PRIxPTR, Images[i].Start);
|
|
swift_reflection_addImage(RC, Images[i].Start);
|
|
}
|
|
|
|
free(Images);
|
|
}
|
|
|
|
#else
|
|
|
|
static swift_reflection_section_t
|
|
makeLocalSection(const void *Buffer,
|
|
swift_remote_reflection_section_t Section) {
|
|
if (Section.Size == 0) {
|
|
swift_reflection_section_t LS = {NULL, NULL};
|
|
return LS;
|
|
}
|
|
|
|
swift_reflection_section_t LS = {(void *)Buffer,
|
|
(void *)((uint8_t *)Buffer + Section.Size)};
|
|
return LS;
|
|
}
|
|
|
|
static swift_reflection_mapping_info_t makeNonContiguousReflectionInfo(
|
|
swift_remote_reflection_section_t *remote_sections,
|
|
swift_reflection_section_t *local_sections) {
|
|
swift_reflection_section_mapping_t sections[SECTIONS_PER_INFO];
|
|
for (size_t i = 0; i < SECTIONS_PER_INFO; ++i) {
|
|
swift_reflection_section_mapping_t section = {local_sections[i],
|
|
remote_sections[i]};
|
|
sections[i] = section;
|
|
}
|
|
|
|
swift_reflection_mapping_info_t ReflectionMappingInfo = {
|
|
sections[0], sections[1], sections[2],
|
|
sections[3], sections[4], sections[5]};
|
|
|
|
return ReflectionMappingInfo;
|
|
}
|
|
|
|
static swift_remote_reflection_section_t
|
|
makeRemoteSection(const PipeMemoryReader *Reader) {
|
|
uintptr_t Start;
|
|
size_t Size;
|
|
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, &Start, sizeof(Start));
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, &Size, sizeof(Size));
|
|
|
|
swift_remote_reflection_section_t RS = {Start, Size};
|
|
DEBUG_LOG("Making remote section with Start = 0x%" PRIxPTR
|
|
" End = 0x%" PRIxPTR " and Size = %lu",
|
|
RS.StartAddress, RS.StartAddress + RS.Size, RS.Size);
|
|
|
|
return RS;
|
|
}
|
|
|
|
static const void *PipeMemoryReader_readRemoteSection(
|
|
const PipeMemoryReader *Reader,
|
|
swift_remote_reflection_section_t *RemoteSection, void **outFreeContext) {
|
|
|
|
const void *Buffer =
|
|
PipeMemoryReader_readBytes((void *)Reader, RemoteSection->StartAddress,
|
|
RemoteSection->Size, outFreeContext);
|
|
if (!Buffer)
|
|
errorAndExit("Couldn't read reflection information");
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
static void
|
|
PipeMemoryReader_receiveReflectionInfo(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader *Reader) {
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
write(WriteFD, REQUEST_REFLECTION_INFO, 2);
|
|
size_t NumReflectionInfos;
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, &NumReflectionInfos,
|
|
sizeof(NumReflectionInfos));
|
|
|
|
if (NumReflectionInfos == 0)
|
|
return;
|
|
|
|
swift_remote_reflection_section_t *RemoteSections =
|
|
calloc(NumReflectionInfos * SECTIONS_PER_INFO,
|
|
sizeof(swift_remote_reflection_section_t));
|
|
if (RemoteSections == NULL)
|
|
errnoAndExit("malloc failed");
|
|
|
|
// We first read all remote reflection sections, there are 6 for every
|
|
// complete reflection info.
|
|
// They come ordered as: fieldmd, assocty, builtin, capture, typeref, reflstr.
|
|
for (size_t i = 0; i < NumReflectionInfos * SECTIONS_PER_INFO; ++i) {
|
|
RemoteSections[i] = makeRemoteSection(Reader);
|
|
}
|
|
// Now pull in the remote sections into our address space.
|
|
swift_reflection_section_t *LocalSections =
|
|
calloc(NumReflectionInfos * SECTIONS_PER_INFO,
|
|
sizeof(swift_reflection_section_t));
|
|
for (size_t i = 0; i < NumReflectionInfos * SECTIONS_PER_INFO; ++i) {
|
|
void *outFreeContext = NULL;
|
|
const void *Buffer = PipeMemoryReader_readRemoteSection(
|
|
(void *)Reader, &RemoteSections[i], &outFreeContext);
|
|
LocalSections[i] = makeLocalSection(Buffer, RemoteSections[i]);
|
|
}
|
|
|
|
// Finally, we zip them in a complete reflection info, with a stride of 6.
|
|
for (size_t i = 0; i < NumReflectionInfos * SECTIONS_PER_INFO;
|
|
i += SECTIONS_PER_INFO) {
|
|
swift_reflection_mapping_info_t Info =
|
|
makeNonContiguousReflectionInfo(&RemoteSections[i], &LocalSections[i]);
|
|
swift_reflection_addReflectionMappingInfo(RC, Info);
|
|
}
|
|
|
|
free(RemoteSections);
|
|
free(LocalSections);
|
|
}
|
|
#endif
|
|
|
|
uint64_t PipeMemoryReader_getStringLength(void *Context, swift_addr_t Address) {
|
|
const PipeMemoryReader *Reader = (const PipeMemoryReader *)Context;
|
|
int WriteFD = PipeMemoryReader_getParentWriteFD(Reader);
|
|
uintptr_t TargetAddress = (uintptr_t)Address;
|
|
write(WriteFD, REQUEST_STRING_LENGTH, 2);
|
|
write(WriteFD, &TargetAddress, sizeof(TargetAddress));
|
|
uintptr_t Length = 0;
|
|
PipeMemoryReader_collectBytesFromPipe(Reader, &Length, sizeof(Length));
|
|
return Length;
|
|
}
|
|
|
|
int reflectHeapObject(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader Pipe) {
|
|
uintptr_t instance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
|
|
if (instance == 0) {
|
|
// Child has no more instances to examine
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
printf("Instance pointer in child address space: 0x%lx\n",
|
|
instance);
|
|
|
|
swift_typeref_t TR = swift_reflection_typeRefForInstance(RC, instance);
|
|
|
|
printf("Type reference:\n");
|
|
swift_reflection_dumpTypeRef(TR);
|
|
printf("\n");
|
|
|
|
printf("Type info:\n");
|
|
swift_reflection_dumpInfoForInstance(RC, instance);
|
|
|
|
printf("\n");
|
|
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1;
|
|
}
|
|
|
|
int reflectExistential(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader Pipe,
|
|
swift_typeref_t MockExistentialTR) {
|
|
uintptr_t instance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
|
|
if (instance == 0) {
|
|
// Child has no more instances to examine
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
printf("Instance pointer in child address space: 0x%lx\n",
|
|
instance);
|
|
|
|
swift_typeref_t InstanceTypeRef;
|
|
swift_addr_t StartOfInstanceData = 0;
|
|
|
|
if (!swift_reflection_projectExistential(RC, instance, MockExistentialTR,
|
|
&InstanceTypeRef,
|
|
&StartOfInstanceData)) {
|
|
printf("swift_reflection_projectExistential failed.\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
|
|
printf("Type reference:\n");
|
|
swift_reflection_dumpTypeRef(InstanceTypeRef);
|
|
printf("\n");
|
|
|
|
printf("Type info:\n");
|
|
swift_reflection_dumpInfoForTypeRef(RC, InstanceTypeRef);
|
|
printf("\n");
|
|
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1;
|
|
}
|
|
|
|
int reflectEnum(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader Pipe) {
|
|
static const char Name[] = MANGLING_PREFIX_STR "ypD";
|
|
swift_typeref_t AnyTR
|
|
= swift_reflection_typeRefForMangledTypeName(
|
|
RC, Name, sizeof(Name)-1);
|
|
|
|
uintptr_t AnyInstance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
|
|
if (AnyInstance == 0) {
|
|
// Child has no more instances to examine
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
swift_typeref_t EnumTypeRef;
|
|
swift_addr_t EnumInstance = 0;
|
|
if (!swift_reflection_projectExistential(RC, AnyInstance, AnyTR,
|
|
&EnumTypeRef,
|
|
&EnumInstance)) {
|
|
printf("swift_reflection_projectExistential failed.\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
|
|
printf("Instance pointer in child address space: 0x%lx\n",
|
|
(uintptr_t)EnumInstance);
|
|
|
|
printf("Type reference:\n");
|
|
swift_reflection_dumpTypeRef(EnumTypeRef);
|
|
printf("\n");
|
|
|
|
printf("Type info:\n");
|
|
swift_reflection_dumpInfoForTypeRef(RC, EnumTypeRef);
|
|
printf("\n");
|
|
|
|
printf("Enum value:\n");
|
|
swift_typeinfo_t InstanceTypeInfo = swift_reflection_infoForTypeRef(RC, EnumTypeRef);
|
|
if (InstanceTypeInfo.Kind != SWIFT_NO_PAYLOAD_ENUM
|
|
&& InstanceTypeInfo.Kind != SWIFT_SINGLE_PAYLOAD_ENUM
|
|
&& InstanceTypeInfo.Kind != SWIFT_MULTI_PAYLOAD_ENUM) {
|
|
// Enums with a single payload case and no non-payload cases
|
|
// can get rewritten by the compiler to just the payload
|
|
// type.
|
|
swift_reflection_dumpInfoForTypeRef(RC, EnumTypeRef);
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1;
|
|
}
|
|
|
|
int CaseIndex;
|
|
if (!swift_reflection_projectEnumValue(RC, EnumInstance, EnumTypeRef, &CaseIndex)) {
|
|
printf("swift_reflection_projectEnumValue failed.\n\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1; // <<< Test cases also verify failures, so this must "succeed"
|
|
}
|
|
if ((unsigned)CaseIndex > InstanceTypeInfo.NumFields) {
|
|
printf("swift_reflection_projectEnumValue returned invalid case.\n\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
|
|
swift_childinfo_t CaseInfo
|
|
= swift_reflection_childOfTypeRef(RC, EnumTypeRef, CaseIndex);
|
|
|
|
if (CaseInfo.TR == 0) {
|
|
// Enum case has no payload
|
|
printf("(enum_value name=%s index=%llu)\n",
|
|
CaseInfo.Name, (unsigned long long)CaseIndex);
|
|
} else {
|
|
printf("(enum_value name=%s index=%llu\n",
|
|
CaseInfo.Name, (unsigned long long)CaseIndex);
|
|
swift_reflection_dumpTypeRef(CaseInfo.TR);
|
|
printf(")\n");
|
|
}
|
|
printf("\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1;
|
|
}
|
|
|
|
int reflectEnumValue(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader Pipe) {
|
|
static const char Name[] = MANGLING_PREFIX_STR "ypD";
|
|
swift_typeref_t AnyTR
|
|
= swift_reflection_typeRefForMangledTypeName(
|
|
RC, Name, sizeof(Name)-1);
|
|
|
|
uintptr_t AnyInstance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
|
|
if (AnyInstance == 0) {
|
|
// Child has no more instances to examine
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
swift_typeref_t EnumTypeRef;
|
|
swift_addr_t EnumInstance = 0;
|
|
if (!swift_reflection_projectExistential(RC, AnyInstance, AnyTR,
|
|
&EnumTypeRef,
|
|
&EnumInstance)) {
|
|
printf("swift_reflection_projectExistential failed.\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
|
|
printf("Type reference:\n");
|
|
swift_reflection_dumpTypeRef(EnumTypeRef);
|
|
|
|
printf("Value: ");
|
|
int parens = 0;
|
|
while (EnumTypeRef != 0) {
|
|
swift_typeinfo_t EnumTypeInfo = swift_reflection_infoForTypeRef(RC, EnumTypeRef);
|
|
if (EnumTypeInfo.Kind != SWIFT_NO_PAYLOAD_ENUM
|
|
&& EnumTypeInfo.Kind != SWIFT_SINGLE_PAYLOAD_ENUM
|
|
&& EnumTypeInfo.Kind != SWIFT_MULTI_PAYLOAD_ENUM) {
|
|
if (parens == 0) {
|
|
printf(".??"); // Enum was optimized away, print "something"
|
|
} else {
|
|
printf("_");
|
|
}
|
|
break;
|
|
}
|
|
|
|
int CaseIndex;
|
|
if (!swift_reflection_projectEnumValue(RC, EnumInstance, EnumTypeRef, &CaseIndex)) {
|
|
printf("swift_reflection_projectEnumValue failed.\n\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1; // <<< Test cases rely on detecting this, so must "succeed"
|
|
}
|
|
if ((unsigned)CaseIndex > EnumTypeInfo.NumFields) {
|
|
printf("swift_reflection_projectEnumValue returned invalid case.\n\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 0;
|
|
}
|
|
|
|
swift_childinfo_t CaseInfo
|
|
= swift_reflection_childOfTypeRef(RC, EnumTypeRef, CaseIndex);
|
|
printf(".%s", CaseInfo.Name);
|
|
EnumTypeRef = CaseInfo.TR;
|
|
if (EnumTypeRef != 0) {
|
|
printf("(");
|
|
parens += 1;
|
|
}
|
|
}
|
|
for (int i = 0; i < parens; ++i) {
|
|
printf(")");
|
|
}
|
|
printf("\n\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
return 1;
|
|
|
|
}
|
|
|
|
static void
|
|
asyncTaskIterationCallback(swift_reflection_ptr_t AllocationPtr, unsigned Count,
|
|
swift_async_task_allocation_chunk_t Chunks[],
|
|
void *ContextPtr) {
|
|
printf(" Allocation block %#" PRIx64 "\n", (uint64_t)AllocationPtr);
|
|
for (unsigned i = 0; i < Count; i++)
|
|
printf(" Chunk at %#" PRIx64 " length %u kind %u\n",
|
|
(uint64_t)Chunks[i].Start, Chunks[i].Length, Chunks[i].Kind);
|
|
}
|
|
|
|
int reflectAsyncTask(SwiftReflectionContextRef RC,
|
|
const PipeMemoryReader Pipe) {
|
|
uintptr_t AsyncTaskInstance = PipeMemoryReader_receiveInstanceAddress(&Pipe);
|
|
printf("Async task %#" PRIx64 "\n", (uint64_t)AsyncTaskInstance);
|
|
swift_reflection_iterateAsyncTaskAllocations(
|
|
RC, AsyncTaskInstance, asyncTaskIterationCallback, NULL);
|
|
|
|
printf("\n\n");
|
|
PipeMemoryReader_sendDoneMessage(&Pipe);
|
|
fflush(stdout);
|
|
return 1;
|
|
}
|
|
|
|
|
|
int doDumpHeapInstance(const char *BinaryFilename) {
|
|
PipeMemoryReader Pipe = createPipeMemoryReader();
|
|
|
|
#if defined(_WIN32)
|
|
#else
|
|
pid_t pid = _fork();
|
|
switch (pid) {
|
|
case -1:
|
|
errnoAndExit("Couldn't fork child process");
|
|
case 0: { // Child:
|
|
close(PipeMemoryReader_getParentWriteFD(&Pipe));
|
|
close(PipeMemoryReader_getParentReadFD(&Pipe));
|
|
dup2(PipeMemoryReader_getChildReadFD(&Pipe), STDIN_FILENO);
|
|
dup2(PipeMemoryReader_getChildWriteFD(&Pipe), STDOUT_FILENO);
|
|
|
|
char *const argv[] = {strdup(BinaryFilename), NULL};
|
|
int r = _execv(BinaryFilename, argv);
|
|
int status = EXIT_SUCCESS;
|
|
if (r == -1) {
|
|
perror("child process");
|
|
status = EXIT_FAILURE;
|
|
}
|
|
exit(status);
|
|
}
|
|
default: { // Parent
|
|
close(PipeMemoryReader_getChildReadFD(&Pipe));
|
|
close(PipeMemoryReader_getChildWriteFD(&Pipe));
|
|
SwiftReflectionContextRef RC =
|
|
swift_reflection_createReflectionContextWithDataLayout(
|
|
(void *)&Pipe, PipeMemoryReader_queryDataLayout,
|
|
PipeMemoryReader_freeBytes, PipeMemoryReader_readBytes,
|
|
PipeMemoryReader_getStringLength,
|
|
PipeMemoryReader_getSymbolAddress);
|
|
|
|
uint8_t PointerSize = PipeMemoryReader_getPointerSize((void*)&Pipe);
|
|
if (PointerSize != sizeof(uintptr_t))
|
|
errorAndExit("Child process had unexpected architecture");
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
PipeMemoryReader_receiveImages(RC, &Pipe);
|
|
#else
|
|
PipeMemoryReader_receiveReflectionInfo(RC, &Pipe);
|
|
#endif
|
|
|
|
while (1) {
|
|
InstanceKind Kind = PipeMemoryReader_receiveInstanceKind(&Pipe);
|
|
switch (Kind) {
|
|
case Object:
|
|
printf("Reflecting an object.\n");
|
|
if (!reflectHeapObject(RC, Pipe))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
case Existential: {
|
|
static const char Name[] = MANGLING_PREFIX_STR "ypD";
|
|
swift_typeref_t AnyTR
|
|
= swift_reflection_typeRefForMangledTypeName(RC,
|
|
Name, sizeof(Name)-1);
|
|
|
|
printf("Reflecting an existential.\n");
|
|
if (!reflectExistential(RC, Pipe, AnyTR))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
}
|
|
case ErrorExistential: {
|
|
static const char ErrorName[] = MANGLING_PREFIX_STR "s5Error_pD";
|
|
swift_typeref_t ErrorTR
|
|
= swift_reflection_typeRefForMangledTypeName(RC,
|
|
ErrorName, sizeof(ErrorName)-1);
|
|
printf("Reflecting an error existential.\n");
|
|
if (!reflectExistential(RC, Pipe, ErrorTR))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
}
|
|
case Closure:
|
|
printf("Reflecting a closure.\n");
|
|
if (!reflectHeapObject(RC, Pipe))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
case Enum: {
|
|
printf("Reflecting an enum.\n");
|
|
if (!reflectEnum(RC, Pipe))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
}
|
|
case EnumValue: {
|
|
printf("Reflecting an enum value.\n");
|
|
if (!reflectEnumValue(RC, Pipe))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
}
|
|
case AsyncTask: {
|
|
printf("Reflecting an async task.\n");
|
|
if (!reflectAsyncTask(RC, Pipe))
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
}
|
|
case None:
|
|
swift_reflection_destroyReflectionContext(RC);
|
|
printf("Done.\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#include <dlfcn.h>
|
|
static unsigned long long computeClassIsSwiftMask(void) {
|
|
uintptr_t *objc_debug_swift_stable_abi_bit_ptr =
|
|
(uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit");
|
|
return objc_debug_swift_stable_abi_bit_ptr ?
|
|
*objc_debug_swift_stable_abi_bit_ptr : 1;
|
|
}
|
|
#else
|
|
static unsigned long long computeClassIsSwiftMask(void) {
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
void printUsageAndExit() {
|
|
fprintf(stderr, "swift-reflection-test <binary filename>\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc != 2)
|
|
printUsageAndExit();
|
|
|
|
const char *BinaryFilename = argv[1];
|
|
|
|
#if defined(_WIN32)
|
|
// FIXME(compnerd) weak linking is not permitted on PE/COFF, we should fall
|
|
// back to GetProcAddress to see if the symbol is present.
|
|
#else
|
|
// swift_reflection_classIsSwiftMask is weak linked so we can work
|
|
// with older Remote Mirror dylibs.
|
|
if (&swift_reflection_classIsSwiftMask != NULL)
|
|
swift_reflection_classIsSwiftMask = computeClassIsSwiftMask();
|
|
#endif
|
|
|
|
uint16_t Version = swift_reflection_getSupportedMetadataVersion();
|
|
printf("Metadata version: %u\n", Version);
|
|
|
|
return doDumpHeapInstance(BinaryFilename);
|
|
}
|