blob: a590b3da1a087cfb889330fd67d0984365d01537 [file] [log] [blame]
//===--- ImageInspectionWin32.cpp - Win32 image inspection ----------------===//
//
// 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 includes routines that interact with the Win32 API on
// Windows platforms to extract runtime metadata embedded in executables and
// DLLs generated by the Swift compiler.
//
//===----------------------------------------------------------------------===//
#if defined(_WIN32) || defined(__CYGWIN__)
#include "ImageInspection.h"
#include "swift/Runtime/Debug.h"
#include <cstdlib>
#include <cstring>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <psapi.h>
#if defined(__CYGWIN__)
#include <dlfcn.h>
#endif
using namespace swift;
/// PE section name for the section that contains protocol conformance records.
static const char ProtocolConformancesSection[] = ".sw2prtc";
/// PE section name for the section that contains type metadata records.
static const char TypeMetadataRecordsSection[] = ".sw2tymd";
/// Context information passed down from _swift_dl_iterate_phdr to the
/// callback function.
struct InspectArgs {
void (*fnAddImageBlock)(const void *, uintptr_t);
const char *sectionName;
};
struct _swift_dl_phdr_info {
void *dlpi_addr;
const char *dlpi_name;
};
static int _swift_dl_iterate_phdr(int (*Callback)(struct _swift_dl_phdr_info *info,
size_t size, const void *data),
const void *data) {
DWORD procId = GetCurrentProcessId();
HANDLE procHandle =
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, procId);
if (!procHandle) {
swift::fatalError(/* flags = */ 0, "OpenProcess() failed");
return 0;
}
int lastRet = 0;
std::vector<HMODULE> modules(1024);
DWORD neededSize;
BOOL ret = EnumProcessModules(procHandle, modules.data(),
modules.size() * sizeof(HMODULE), &neededSize);
if (!ret) {
swift::fatalError(/* flags = */ 0, "EnumProcessModules() failed");
return 0;
}
if (modules.size() * sizeof(HMODULE) < neededSize) {
modules.resize(neededSize / sizeof(HMODULE));
ret = EnumProcessModules(procHandle, modules.data(),
modules.size() * sizeof(HMODULE), &neededSize);
}
if (!ret) {
swift::fatalError(/* flags = */ 0, "EnumProcessModules() failed");
return 0;
}
for (unsigned i = 0; i < neededSize / sizeof(HMODULE); i++) {
char modName[MAX_PATH];
if (!GetModuleFileNameExA(procHandle, modules[i], modName,
sizeof(modName))) {
swift::fatalError(/* flags = */ 0, "GetModuleFileNameExA() failed");
}
_swift_dl_phdr_info hdr;
hdr.dlpi_name = modName;
hdr.dlpi_addr = modules[i];
lastRet = Callback(&hdr, sizeof(hdr), data);
if (lastRet != 0)
break;
}
CloseHandle(procHandle);
return lastRet;
}
static uint8_t *_swift_getSectionDataPE(const void *handle,
const char *sectionName,
unsigned long *sectionSize) {
// In Cygwin, dlopen() returns PE/COFF image pointer.
// This is relying on undocumented feature of Windows API LoadLibrary().
const unsigned char *peStart = static_cast<const unsigned char *>(handle);
const int kLocationOfNtHeaderOffset = 0x3C;
int ntHeadersOffset =
*reinterpret_cast<const int32_t *>(peStart + kLocationOfNtHeaderOffset);
bool assert1 =
peStart[ntHeadersOffset] == 'P' && peStart[ntHeadersOffset + 1] == 'E';
if (!assert1) {
swift::fatalError(/* flags = */ 0, "_swift_getSectionDataPE()'s finding PE failed");
}
const unsigned char *coff = peStart + ntHeadersOffset + 4;
int16_t numberOfSections = *reinterpret_cast<const int16_t *>(coff + 2);
// SizeOfOptionalHeader
int16_t sizeOfOptionalHeader = *reinterpret_cast<const int16_t *>(coff + 16);
const int kCoffFileHeaderSize = 20;
const unsigned char *sectionTableBase =
coff + kCoffFileHeaderSize + sizeOfOptionalHeader;
// Section Header Record
const int kSectionRecordSize = 40;
const unsigned char *sectionHeader = sectionTableBase;
for (int i = 0; i < numberOfSections; i++) {
uint32_t virtualSize =
*reinterpret_cast<const uint32_t *>(&sectionHeader[8]);
uint32_t virtualAddress =
*reinterpret_cast<const uint32_t *>(&sectionHeader[12]);
char nameOfThisSection[9];
memcpy(nameOfThisSection, sectionHeader, 8);
nameOfThisSection[8] = '\0';
if (strcmp(sectionName, nameOfThisSection) == 0) {
*sectionSize = virtualSize;
return const_cast<uint8_t *>(peStart + virtualAddress);
}
sectionHeader += kSectionRecordSize;
}
return nullptr;
}
static int _addImageCallback(struct _swift_dl_phdr_info *info,
size_t size, const void *data) {
const InspectArgs *inspectArgs = static_cast<const InspectArgs *>(data);
// inspectArgs contains addImage*Block function and the section name
#if defined(_WIN32)
HMODULE handle;
if (!info->dlpi_name || info->dlpi_name[0] == '\0')
handle = GetModuleHandle(nullptr);
else
handle = GetModuleHandleA(info->dlpi_name);
#else
void *handle;
if (!info->dlpi_name || info->dlpi_name[0] == '\0')
handle = dlopen(nullptr, RTLD_LAZY);
else
handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
#endif
unsigned long conformancesSize;
const uint8_t *conformances =
_swift_getSectionDataPE(handle, inspectArgs->sectionName,
&conformancesSize);
if (conformances)
inspectArgs->fnAddImageBlock(conformances, conformancesSize);
// There is no close function or free function for GetModuleHandle(),
// especially we should not pass a handle returned by GetModuleHandle to the
// FreeLibrary function.
#if defined(__CYGWIN__)
dlclose(handle);
#endif
return 0;
}
void swift::initializeProtocolConformanceLookup() {
// Search the loaded dls. This only searches the already
// loaded ones.
// FIXME: Find a way to have this continue to happen for dlopen-ed images.
// rdar://problem/19045112
const InspectArgs ProtocolConformancesArgs = {
addImageProtocolConformanceBlockCallback,
ProtocolConformancesSection,
};
_swift_dl_iterate_phdr(_addImageCallback, &ProtocolConformancesArgs);
}
void swift::initializeTypeMetadataRecordLookup() {
// Search the loaded dls. This only searches the already
// loaded ones.
// FIXME: Find a way to have this continue to happen for dlopen-ed images.
// rdar://problem/19045112
const InspectArgs TypeMetadataRecordsArgs = {
addImageTypeMetadataRecordBlockCallback,
TypeMetadataRecordsSection,
};
_swift_dl_iterate_phdr(_addImageCallback, &TypeMetadataRecordsArgs);
}
int swift::lookupSymbol(const void *address, SymbolInfo *info) {
#if defined(__CYGWIN__)
Dl_info dlinfo;
if (dladdr(address, &dlinfo) == 0) {
return 0;
}
info->fileName = dlinfo.dli_fname;
info->baseAddress = dlinfo.dli_fbase;
info->symbolName = dli_info.dli_sname;
info->symbolAddress = dli_saddr;
return 1;
#else
return 0;
#endif // __CYGWIN__
}
#endif // defined(_WIN32) || defined(__CYGWIN__)