Skip to content

Commit c4070cb

Browse files
authored
Merge pull request #25563 from mikeash/dlopen-race-5.1
[5.1][Runtime] Fix a race with dlopen and libobjc.
2 parents f71feb9 + 652d16e commit c4070cb

File tree

3 files changed

+95
-6
lines changed

3 files changed

+95
-6
lines changed

stdlib/public/runtime/ImageInspectionMachO.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "ImageInspection.h"
2424
#include <mach-o/dyld.h>
2525
#include <mach-o/getsect.h>
26+
#include <objc/runtime.h>
2627
#include <assert.h>
2728
#include <dlfcn.h>
2829

@@ -55,7 +56,7 @@ extern "C" void *_NSGetMachExecuteHeader();
5556

5657
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
5758
void CONSUME_BLOCK(const void *start, uintptr_t size)>
58-
void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) {
59+
void addImageCallback(const mach_header *mh) {
5960
#if __POINTER_WIDTH__ == 64
6061
assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!");
6162
#endif
@@ -72,12 +73,17 @@ void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) {
7273

7374
CONSUME_BLOCK(section, size);
7475
}
76+
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
77+
void CONSUME_BLOCK(const void *start, uintptr_t size)>
78+
void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) {
79+
addImageCallback<SEGMENT_NAME, SECTION_NAME, CONSUME_BLOCK>(mh);
80+
}
7581

7682
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
7783
const char *SEGMENT_NAME2, const char *SECTION_NAME2,
7884
void CONSUME_BLOCK(const void *start, uintptr_t size,
7985
const void *start2, uintptr_t size2)>
80-
void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) {
86+
void addImageCallback2Sections(const mach_header *mh) {
8187
#if __POINTER_WIDTH__ == 64
8288
assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!");
8389
#endif
@@ -103,27 +109,48 @@ void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) {
103109

104110
CONSUME_BLOCK(section, size, section2, size2);
105111
}
112+
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
113+
const char *SEGMENT_NAME2, const char *SECTION_NAME2,
114+
void CONSUME_BLOCK(const void *start, uintptr_t size,
115+
const void *start2, uintptr_t size2)>
116+
void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) {
117+
addImageCallback2Sections<SEGMENT_NAME, SECTION_NAME,
118+
SEGMENT_NAME2, SECTION_NAME2,
119+
CONSUME_BLOCK>(mh);
120+
}
121+
106122
} // end anonymous namespace
107123

124+
#if OBJC_ADDLOADIMAGEFUNC_DEFINED
125+
#define REGISTER_FUNC(...) \
126+
if (__builtin_available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)) { \
127+
objc_addLoadImageFunc(__VA_ARGS__); \
128+
} else { \
129+
_dyld_register_func_for_add_image(__VA_ARGS__); \
130+
}
131+
#else
132+
#define REGISTER_FUNC(...) _dyld_register_func_for_add_image(__VA_ARGS__)
133+
#endif
134+
108135
void swift::initializeProtocolLookup() {
109-
_dyld_register_func_for_add_image(
136+
REGISTER_FUNC(
110137
addImageCallback<TextSegment, ProtocolsSection,
111138
addImageProtocolsBlockCallback>);
112139
}
113140

114141
void swift::initializeProtocolConformanceLookup() {
115-
_dyld_register_func_for_add_image(
142+
REGISTER_FUNC(
116143
addImageCallback<TextSegment, ProtocolConformancesSection,
117144
addImageProtocolConformanceBlockCallback>);
118145
}
119146
void swift::initializeTypeMetadataRecordLookup() {
120-
_dyld_register_func_for_add_image(
147+
REGISTER_FUNC(
121148
addImageCallback<TextSegment, TypeMetadataRecordSection,
122149
addImageTypeMetadataRecordBlockCallback>);
123150
}
124151

125152
void swift::initializeDynamicReplacementLookup() {
126-
_dyld_register_func_for_add_image(
153+
REGISTER_FUNC(
127154
addImageCallback2Sections<TextSegment, DynamicReplacementSection,
128155
TextSegment, DynamicReplacementSomeSection,
129156
addImageDynamicReplacementBlockCallback>);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
class C: CustomStringConvertible {
3+
var description: String {
4+
return "description"
5+
}
6+
}

test/stdlib/dlopen_race.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-library -o %t/dlopen_race.dylib %S/Inputs/dlopen_race_dylib.swift
3+
// RUN: %target-build-swift -o %t/dlopen_race %s
4+
// RUN: %target-run %t/dlopen_race
5+
// REQUIRES: executable_test
6+
// REQUIRES: objc_interop
7+
8+
import StdlibUnittest
9+
10+
import Darwin
11+
12+
var DlopenRaceTests = TestSuite("DlopenRace")
13+
14+
typealias add_image_callback = @convention(c) (UnsafeRawPointer?, Int) -> Void
15+
func register_func_for_add_image(_ f: add_image_callback) {
16+
let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
17+
let _dyld_register_func_for_add_image_ptr =
18+
dlsym(RTLD_DEFAULT, "_dyld_register_func_for_add_image")
19+
expectNotNil(_dyld_register_func_for_add_image_ptr)
20+
21+
typealias _dyld_register_func_for_add_image_func =
22+
@convention(c) (add_image_callback) -> Void
23+
let _dyld_register_func_for_add_image = unsafeBitCast(
24+
_dyld_register_func_for_add_image_ptr,
25+
to: _dyld_register_func_for_add_image_func.self)
26+
_dyld_register_func_for_add_image(f)
27+
}
28+
29+
// Make sure Swift doesn't register newly opened images before ObjC is ready
30+
// for them to be used. rdar://problem/49742015
31+
var add_image_count = 0
32+
DlopenRaceTests.test("race") {
33+
// This test is expected to fail unless the ObjC notification is supported.
34+
let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
35+
let objc_addLoadImageFunc = dlsym(RTLD_DEFAULT, "objc_addLoadImageFunc");
36+
if objc_addLoadImageFunc == nil { return }
37+
38+
register_func_for_add_image({ header, slide in
39+
// The protocol conformance check in the print call is enough to trigger
40+
// ObjC class initialization in the newly opened image if Swift has
41+
// registered the conformance records in that image. While we would be
42+
// unlikely to make this sort of call directly in the callback in a real
43+
// program, it could happen at this time in another thread.
44+
print(header, slide)
45+
add_image_count += 1
46+
})
47+
48+
let dylibPath = CommandLine.arguments[0] + ".dylib"
49+
50+
let beforeCount = add_image_count
51+
let handle = dlopen(dylibPath, RTLD_LAZY)
52+
expectNotNil(handle, String(cString: dlerror()))
53+
expectEqual(add_image_count, beforeCount + 1)
54+
}
55+
56+
runAllTests()

0 commit comments

Comments
 (0)