Files
swift-mirror/stdlib/toolchain/Compatibility50/Overrides.cpp
Mike Ash ecd6d4ddec Add a new ConcurrentReadableHashMap type. Switch the protocol conformance cache
to use it.

ConcurrentReadableHashMap is lock-free for readers, with writers using a lock to
ensure mutual exclusion amongst each other. The intent is to eventually replace
all uses ConcurrentMap with ConcurrentReadableHashMap.

ConcurrentReadableHashMap provides for relatively quick lookups by using a hash
table. Rearders perform an atomic increment/decrement in order to inform writers
that there are active readers. The design attempts to minimize wasted memory by
storing the actual elements out-of-line, and having the table store indices into
a separate array of elements.

The protocol conformance cache now uses ConcurrentReadableHashMap, which
provides faster lookups and less memory use than the previous ConcurrentMap
implementation. The previous implementation caches
ProtocolConformanceDescriptors and extracts the WitnessTable after the cache
lookup. The new implementation directly caches the WitnessTable, removing an
extra step (potentially a quite slow one) from the fast path.

The previous implementation used a generational scheme to detect when negative
cache entries became obsolete due to new dynamic libraries being loaded, and
update them in place. The new implementation just clears the entire cache when
libraries are loaded, greatly simplifying the code and saving the memory needed
to track the current generation in each negative cache entry. This means we need
to re-cache all requested conformances after loading a dynamic library, but
loading libraries at runtime is rare and slow anyway.

rdar://problem/67268325
2020-08-20 13:05:30 -04:00

123 lines
4.3 KiB
C++

//===--- Overrides.cpp - Compat override table for Swift 5.0 runtime ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 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 compatibility override hooks for Swift 5.0 runtimes.
//
//===----------------------------------------------------------------------===//
#include "Overrides.h"
#include "../Compatibility51/Overrides.h"
#include "CompatibilityOverride.h"
#include <dlfcn.h>
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
using namespace swift;
struct OverrideSection {
uintptr_t version;
#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \
Override_ ## name name;
#include "CompatibilityOverride.def"
};
OverrideSection Swift50Overrides
__attribute__((used, section("__DATA,__swift_hooks"))) = {
.version = 0,
.conformsToProtocol = swift50override_conformsToProtocol,
// We use the same hook for conformsToSwiftProtocol as we do for a 5.1
// runtime, so reference the override from the Compatibility51 library.
// If we're back deploying to Swift 5.0, we also have to support 5.1, so
// the Compatibility51 library is always linked when the 50 library is.
.conformsToSwiftProtocol = swift51override_conformsToSwiftProtocol,
};
// Allow this library to get force-loaded by autolinking
__attribute__((weak, visibility("hidden")))
extern "C"
char _swift_FORCE_LOAD_$_swiftCompatibility50 = 0;
// Put a getClass hook in front of the system Swift runtime's hook to prevent it
// from trying to interpret symbolic references. rdar://problem/55036306
// FIXME: delete this #if and dlsym once we don't
// need to build with older libobjc headers
#if !OBJC_GETCLASSHOOK_DEFINED
using objc_hook_getClass = BOOL(*)(const char * _Nonnull name,
Class _Nullable * _Nonnull outClass);
#endif
static objc_hook_getClass OldGetClassHook;
static BOOL
getObjCClassByMangledName_untrusted(const char * _Nonnull typeName,
Class _Nullable * _Nonnull outClass) {
// Scan the string for byte sequences that might be recognized as
// symbolic references, and reject them.
for (const char *c = typeName; *c != 0; ++c) {
if (*c >= 1 && *c < 0x20) {
*outClass = Nil;
return NO;
}
}
if (OldGetClassHook) {
return OldGetClassHook(typeName, outClass);
}
// In case the OS runtime for some reason didn't install a hook, fallback to
// NO.
return NO;
}
#if __POINTER_WIDTH__ == 64
using mach_header_platform = mach_header_64;
#else
using mach_header_platform = mach_header;
#endif
__attribute__((constructor))
static void installGetClassHook_untrusted() {
// swiftCompatibility* might be linked into multiple dynamic libraries because
// of build system reasons, but the copy in the main executable is the only
// one that should count. Bail early unless we're running out of the main
// executable.
//
// Newer versions of dyld add additional API that can determine this more
// efficiently, but we have to support back to OS X 10.9/iOS 7, so dladdr
// is the only API that reaches back that far.
Dl_info dlinfo;
if (dladdr((const void*)(uintptr_t)installGetClassHook_untrusted, &dlinfo) == 0)
return;
auto machHeader = (const mach_header_platform *)dlinfo.dli_fbase;
if (machHeader->filetype != MH_EXECUTE)
return;
// FIXME: delete this #if and dlsym once we don't
// need to build with older libobjc headers
#if !OBJC_GETCLASSHOOK_DEFINED
using objc_hook_getClass = BOOL(*)(const char * _Nonnull name,
Class _Nullable * _Nonnull outClass);
auto objc_setHook_getClass =
(void(*)(objc_hook_getClass _Nonnull,
objc_hook_getClass _Nullable * _Nonnull))
dlsym(RTLD_DEFAULT, "objc_setHook_getClass");
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
if (objc_setHook_getClass) {
objc_setHook_getClass(getObjCClassByMangledName_untrusted,
&OldGetClassHook);
}
#pragma clang diagnostic pop
}