Skip to content

Commit 3cc86eb

Browse files
committed
[Runtime] Look up Error bridging symbols indirectly through special symbols that won't be stripped in static builds.
This fixes a problem where error bridging didn't work in stripped executables using the static versions of the Swift libraries. ErrorObject.mm looks up some symbols with dlsym, but stripping makes it so it can't find those. This change makes a separate set of symbols explicitly made for ErrorObject.mm to look up, and marks them as dynamically referenced so stripping won't remove them. Longer term, we'd like a better solution for looking up these symbols, but this will do for now. rdar://problem/39810532
1 parent 6b2821c commit 3cc86eb

File tree

6 files changed

+75
-10
lines changed

6 files changed

+75
-10
lines changed

stdlib/public/SDK/Foundation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD
2626
NSCoder.swift
2727
NSDate.swift
2828
NSDictionary.swift
29+
NSError.c
2930
NSError.swift
3031
NSExpression.swift
3132
NSFastEnumeration.swift
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/Runtime/Config.h"
14+
15+
#if SWIFT_OBJC_INTEROP
16+
#include "swift/Demangling/ManglingMacros.h"
17+
18+
// Declare dynamic lookup points for ErrorObject.mm. It uses dlsym to
19+
// find these symbols to locate NS/CFError Error conformance tables and
20+
// related items. The .desc asm directive ensures that they are
21+
// preserved even when statically linked into an executable and
22+
// stripped, so that the dlsym lookup still works.
23+
#define ERROROBJECT_DYNAMIC_LOOKUP_POINT(symbol) \
24+
extern void *MANGLE_SYM(symbol); \
25+
void **MANGLING_CONCAT2(ErrorObjectLookup_, MANGLE_SYM(symbol)) = &MANGLE_SYM(symbol); \
26+
asm(".desc _ErrorObjectLookup_" MANGLE_AS_STRING(MANGLE_SYM(symbol)) ", 0x10");
27+
28+
ERROROBJECT_DYNAMIC_LOOKUP_POINT(So10CFErrorRefas5Error10FoundationWa)
29+
ERROROBJECT_DYNAMIC_LOOKUP_POINT(So8NSObjectCs8Hashable10ObjectiveCWa)
30+
ERROROBJECT_DYNAMIC_LOOKUP_POINT(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)
31+
ERROROBJECT_DYNAMIC_LOOKUP_POINT(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)
32+
ERROROBJECT_DYNAMIC_LOOKUP_POINT(10Foundation26_ObjectiveCBridgeableErrorMp)
33+
34+
#endif

stdlib/public/runtime/ErrorObject.mm

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,21 @@ static Class getSwiftNativeNSErrorClass() {
220220
object_dispose((id)error);
221221
}
222222

223+
/// Look up a symbol that points to something else. Treats the symbol as
224+
/// a void** and dereferences it if it's non-NULL. Returns NULL if the
225+
/// symbol can't be found or if the value is NULL.
226+
static void *dynamicLookupSymbol(const char *name) {
227+
void **ptr = reinterpret_cast<void **>(dlsym(RTLD_DEFAULT, name));
228+
if (!ptr) return nullptr;
229+
return *ptr;
230+
}
231+
232+
/// Look up an indirect pointer to a mangled Swift symbol, automatically
233+
/// prepending the ErrorObjectLookup_ prefix. Used to find the various
234+
/// Foundation overlay symbols for Error bridging.
235+
#define DYNAMIC_LOOKUP_SYMBOL(symbol) \
236+
dynamicLookupSymbol("ErrorObjectLookup_" MANGLE_AS_STRING(MANGLE_SYM(symbol)))
237+
223238
static const WitnessTable *getNSErrorConformanceToError() {
224239
// CFError and NSError are toll-free-bridged, so we can use either type's
225240
// witness table interchangeably. CFError's is potentially slightly more
@@ -228,8 +243,7 @@ static Class getSwiftNativeNSErrorClass() {
228243
// to assume that that's been linked in if a user is using NSError in their
229244
// Swift source.
230245

231-
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT,
232-
MANGLE_AS_STRING(MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa))));
246+
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(DYNAMIC_LOOKUP_SYMBOL(So10CFErrorRefas5Error10FoundationWa));
233247
assert(TheWitnessTable &&
234248
"Foundation overlay not loaded, or 'CFError : Error' conformance "
235249
"not available");
@@ -238,8 +252,7 @@ static Class getSwiftNativeNSErrorClass() {
238252
}
239253

240254
static const HashableWitnessTable *getNSErrorConformanceToHashable() {
241-
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT,
242-
MANGLE_AS_STRING(MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa))));
255+
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(DYNAMIC_LOOKUP_SYMBOL(So8NSObjectCs8Hashable10ObjectiveCWa));
243256
assert(TheWitnessTable &&
244257
"ObjectiveC overlay not loaded, or 'NSObject : Hashable' conformance "
245258
"not available");
@@ -379,8 +392,7 @@ typedef SWIFT_CC(swift)
379392
// public func Foundation._getErrorDefaultUserInfo<T: Error>(_ error: T)
380393
// -> AnyObject?
381394
auto foundationGetDefaultUserInfo = SWIFT_LAZY_CONSTANT(
382-
reinterpret_cast<GetDefaultFn*> (dlsym(RTLD_DEFAULT,
383-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)))));
395+
reinterpret_cast<GetDefaultFn*> (DYNAMIC_LOOKUP_SYMBOL(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)));
384396
if (!foundationGetDefaultUserInfo) {
385397
SWIFT_CC_PLUSONE_GUARD(T->vw_destroy(error));
386398
return nullptr;
@@ -520,12 +532,10 @@ typedef SWIFT_CC(swift)
520532
bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
521533
const WitnessTable *);
522534
auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT(
523-
reinterpret_cast<BridgeFn*>(dlsym(RTLD_DEFAULT,
524-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)))));
535+
reinterpret_cast<BridgeFn*>(DYNAMIC_LOOKUP_SYMBOL(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)));
525536
// protocol _ObjectiveCBridgeableError
526537
auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT(
527-
reinterpret_cast<const ProtocolDescriptor *>(dlsym(RTLD_DEFAULT,
528-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp)))));
538+
reinterpret_cast<const ProtocolDescriptor *>(DYNAMIC_LOOKUP_SYMBOL(10Foundation26_ObjectiveCBridgeableErrorMp)));
529539

530540
// If the Foundation overlay isn't loaded, then arbitrary NSErrors can't be
531541
// bridged.

test/stdlib/ErrorBridgedStatic.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class Bar: Foo {
1414
override func foo(_ x: Int32) throws {
1515
try super.foo(5)
1616
}
17+
18+
override func foothrows(_ x: Int32) throws {
19+
try super.foothrows(5)
20+
}
1721
}
1822

1923
var ErrorBridgingStaticTests = TestSuite("ErrorBridging with static libs")
@@ -24,4 +28,14 @@ ErrorBridgingStaticTests.test("round-trip Swift override of ObjC method") {
2428
} catch { }
2529
}
2630

31+
ErrorBridgingStaticTests.test("round-trip Swift override of throwing ObjC method") {
32+
do {
33+
try (Bar() as Foo).foothrows(5)
34+
} catch {
35+
print(error)
36+
expectEqual(error._domain, "abcd")
37+
expectEqual(error._code, 1234)
38+
}
39+
}
40+
2741
runAllTests()

test/stdlib/Inputs/ErrorBridgedStaticImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
@interface Foo: NSObject
44

55
- (BOOL)foo:(int)x error:(NSError**)error;
6+
- (BOOL)foothrows:(int)x error:(NSError**)error;
67

78
@end

test/stdlib/Inputs/ErrorBridgedStaticImpl.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ - (BOOL)foo:(int)x error:(NSError**)error {
77
return NO;
88
}
99

10+
- (BOOL)foothrows:(int)x error:(NSError**)error {
11+
*error = [NSError errorWithDomain: @"abcd" code: 1234 userInfo: nil];
12+
return NO;
13+
}
14+
1015
@end
1116

0 commit comments

Comments
 (0)