Skip to content

Commit e07c0d2

Browse files
committed
[runtime] Binary section data loading for extra ELF images
- For ELF targets, keep track of shared objects as they are dynamically loaded so that section data can be added to the protocol conformance and type metadata caches after initialisation (rdar://problem/19045112).
1 parent 1860fec commit e07c0d2

File tree

5 files changed

+181
-55
lines changed

5 files changed

+181
-55
lines changed

stdlib/public/runtime/CMakeLists.txt

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ set(LLVM_OPTIONAL_SOURCES
6060
MutexPThread.cpp
6161
MutexWin32.cpp
6262
CygwinPort.cpp
63+
ImageInspectionInit.cpp
6364
ImageInspectionELF.cpp
6465
ImageInspectionStatic.cpp
6566
StaticBinaryELF.cpp
@@ -129,6 +130,12 @@ foreach(sdk ${SWIFT_CONFIGURED_SDKS})
129130
endif()
130131
endforeach()
131132

133+
add_swift_library(section_magic_loader OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE
134+
ImageInspectionInit.cpp
135+
C_COMPILE_FLAGS ${swift_runtime_compile_flags}
136+
TARGET_SDKS "${ELFISH_SDKS}"
137+
LINK_FLAGS ${swift_runtime_linker_flags}
138+
INSTALL_IN_COMPONENT never_install)
132139
add_swift_library(section_magic_begin OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE
133140
swift_sections.S
134141
C_COMPILE_FLAGS ${swift_runtime_compile_flags} "-DSWIFT_BEGIN"
@@ -148,26 +155,40 @@ foreach(sdk ${ELFISH_SDKS})
148155
set(arch_subdir "${SWIFT_SDK_${sdk}_LIB_SUBDIR}/${arch}")
149156
set(arch_suffix "${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}")
150157

151-
set(section_magic_begin_name "section_magic_begin-${arch_suffix}")
152-
set(section_magic_end_name "section_magic_end-${arch_suffix}")
158+
set(section_magic_loader_obj "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/section_magic_loader-${arch_suffix}.dir/ImageInspectionInit.cpp${CMAKE_C_OUTPUT_EXTENSION}")
159+
set(section_magic_begin_obj "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/section_magic_begin-${arch_suffix}.dir/swift_sections.S${CMAKE_C_OUTPUT_EXTENSION}")
160+
set(section_magic_end_obj "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/section_magic_end-${arch_suffix}.dir/swift_sections.S${CMAKE_C_OUTPUT_EXTENSION}")
153161

154-
add_custom_command_target(section_magic_${arch_suffix}_objects
162+
if(SWIFT_ENABLE_GOLD_LINKER)
163+
set(LD_COMMAND "gold")
164+
else()
165+
set(LD_COMMAND "ld")
166+
endif()
167+
168+
add_custom_command_target(section_magic_${arch_suffix}_begin_object
155169
COMMAND
156-
"${CMAKE_COMMAND}" -E copy
157-
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${section_magic_begin_name}.dir/swift_sections.S${CMAKE_C_OUTPUT_EXTENSION}"
170+
# Merge ImageInspectionInit.o + swift_sections.S(BEGIN) => swift_begin.o
171+
${LD_COMMAND} -r -o "${SWIFTLIB_DIR}/${arch_subdir}/swift_begin.o"
172+
"${section_magic_begin_obj}" "${section_magic_loader_obj}"
173+
OUTPUT
158174
"${SWIFTLIB_DIR}/${arch_subdir}/swift_begin.o"
175+
DEPENDS
176+
"${section_magic_begin_obj}"
177+
"${section_magic_loader_obj}")
178+
179+
add_custom_command_target(section_magic_${arch_suffix}_end_object
159180
COMMAND
160181
"${CMAKE_COMMAND}" -E copy
161-
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${section_magic_end_name}.dir/swift_sections.S${CMAKE_C_OUTPUT_EXTENSION}"
182+
"${section_magic_end_obj}"
162183
"${SWIFTLIB_DIR}/${arch_subdir}/swift_end.o"
163184
OUTPUT
164-
"${SWIFTLIB_DIR}/${arch_subdir}/swift_begin.o"
165185
"${SWIFTLIB_DIR}/${arch_subdir}/swift_end.o"
166186
DEPENDS
167-
${section_magic_begin_name}
168-
${section_magic_end_name})
187+
"${section_magic_end_obj}")
169188

170-
list(APPEND object_target_list "${section_magic_${arch_suffix}_objects}")
189+
list(APPEND object_target_list
190+
"${section_magic_${arch_suffix}_begin_object}"
191+
"${section_magic_${arch_suffix}_end_object}")
171192

172193
swift_install_in_component(stdlib
173194
FILES "${SWIFTLIB_DIR}/${arch_subdir}/swift_begin.o" "${SWIFTLIB_DIR}/${arch_subdir}/swift_end.o"

stdlib/public/runtime/ImageInspection.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ namespace swift {
3030
void *symbolAddress;
3131
};
3232

33+
#if defined(__ELF__) || defined(__ANDROID__)
34+
35+
struct SectionInfo {
36+
uint64_t size;
37+
const char *data;
38+
};
39+
40+
// Called by injected constructors when a dynamic library is loaded.
41+
void addNewDSOImage(const void *addr);
42+
43+
#endif // defined(__ELF__) || defined(__ANDROID__)
44+
3345
/// Load the metadata from the image necessary to find a type's
3446
/// protocol conformance.
3547
void initializeProtocolConformanceLookup();

stdlib/public/runtime/ImageInspectionELF.cpp

Lines changed: 97 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#if defined(__ELF__) || defined(__ANDROID__)
2020

2121
#include "ImageInspection.h"
22+
#include "../SwiftShims/Visibility.h"
23+
#include <atomic>
2224
#include <elf.h>
2325
#include <link.h>
2426
#include <dlfcn.h>
@@ -35,67 +37,124 @@ static const char ProtocolConformancesSymbol[] =
3537
static const char TypeMetadataRecordsSymbol[] =
3638
".swift2_type_metadata_start";
3739

40+
static std::atomic<bool> didInitializeProtocolConformanceLookup;
41+
static std::atomic<bool> didInitializeTypeMetadataLookup;
42+
3843
/// Context arguments passed down from dl_iterate_phdr to its callback.
3944
struct InspectArgs {
4045
/// Symbol name to look up.
4146
const char *symbolName;
4247
/// Callback function to invoke with the metadata block.
4348
void (*addBlock)(const void *start, uintptr_t size);
49+
/// Set to true when initialize*Lookup() is called.
50+
std::atomic<bool> *didInitializeLookup;
4451
};
4552

46-
static int iteratePHDRCallback(struct dl_phdr_info *info,
47-
size_t size, void *data) {
48-
const InspectArgs *inspectArgs = reinterpret_cast<const InspectArgs *>(data);
49-
void *handle;
50-
if (!info->dlpi_name || info->dlpi_name[0] == '\0') {
51-
handle = dlopen(nullptr, RTLD_LAZY);
52-
} else {
53-
handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
54-
}
53+
static InspectArgs ProtocolConformanceArgs = {
54+
ProtocolConformancesSymbol,
55+
addImageProtocolConformanceBlockCallback,
56+
&didInitializeProtocolConformanceLookup
57+
};
5558

56-
if (!handle) {
57-
// Not a shared library.
58-
return 0;
59-
}
59+
static InspectArgs TypeMetadataRecordArgs = {
60+
TypeMetadataRecordsSymbol,
61+
addImageTypeMetadataRecordBlockCallback,
62+
&didInitializeTypeMetadataLookup
63+
};
6064

61-
const char *conformances =
62-
reinterpret_cast<const char*>(dlsym(handle, inspectArgs->symbolName));
6365

64-
if (!conformances) {
65-
// if there are no conformances, don't hold this handle open.
66+
// Extract the section information for a named section in an image. imageName
67+
// can be nullptr to specify the main executable.
68+
static SectionInfo getSectionInfo(const char *imageName,
69+
const char *sectionName) {
70+
SectionInfo sectionInfo = { 0, nullptr };
71+
void *handle = dlopen(imageName, RTLD_LAZY);
72+
if (handle) {
73+
void *symbol = dlsym(handle, sectionName);
74+
if (symbol) {
75+
// Extract the size of the section data from the head of the section.
76+
const char *section = reinterpret_cast<const char *>(symbol);
77+
memcpy(&sectionInfo.size, section, sizeof(uint64_t));
78+
sectionInfo.data = section + sizeof(uint64_t);
79+
}
6680
dlclose(handle);
81+
}
82+
return sectionInfo;
83+
}
84+
85+
static int iteratePHDRCallback(struct dl_phdr_info *info,
86+
size_t size, void *data) {
87+
const InspectArgs *inspectArgs = reinterpret_cast<const InspectArgs *>(data);
88+
const char *fname = info->dlpi_name;
89+
90+
// While dl_iterate_phdr() is in progress it holds a lock to prevent other
91+
// images being loaded. The initialize flag is set here inside the callback so
92+
// that addNewDSOImage() sees a consistent state. If it was set outside the
93+
// dl_interate_phdr() call then it could result in images being missed or
94+
// added twice.
95+
inspectArgs->didInitializeLookup->store(true, std::memory_order_release);
96+
97+
if (fname == nullptr || fname[0] == '\0') {
98+
// The filename may be null for both the dynamic loader and main executable.
99+
// So ignore null image name here and explictly add the main executable
100+
// in initialize*Lookup() to avoid adding the data twice.
67101
return 0;
68102
}
69103

70-
// Extract the size of the conformances block from the head of the section.
71-
uint64_t conformancesSize;
72-
memcpy(&conformancesSize, conformances, sizeof(conformancesSize));
73-
conformances += sizeof(conformancesSize);
104+
SectionInfo block = getSectionInfo(fname, inspectArgs->symbolName);
105+
if (block.size > 0) {
106+
inspectArgs->addBlock(block.data, block.size);
107+
}
108+
return 0;
109+
}
74110

75-
inspectArgs->addBlock(conformances, conformancesSize);
111+
// Add the section information in an image specified by an address in that
112+
// image.
113+
static void addBlockInImage(const InspectArgs *inspectArgs, const void *addr) {
114+
const char *fname = nullptr;
115+
if (addr) {
116+
Dl_info info;
117+
if (dladdr(addr, &info) == 0 || info.dli_fname == nullptr) {
118+
return;
119+
}
120+
fname = info.dli_fname;
121+
}
122+
SectionInfo block = getSectionInfo(fname, inspectArgs->symbolName);
123+
if (block.size > 0) {
124+
inspectArgs->addBlock(block.data, block.size);
125+
}
126+
}
76127

77-
dlclose(handle);
78-
return 0;
128+
static void initializeSectionLookup(InspectArgs *inspectArgs) {
129+
// Add section data in the main executable.
130+
addBlockInImage(inspectArgs, nullptr);
131+
// Search the loaded dls. This only searches the already
132+
// loaded ones. Any images loaded after this are processed by
133+
// addNewDSOImage() below.
134+
dl_iterate_phdr(iteratePHDRCallback, reinterpret_cast<void *>(inspectArgs));
79135
}
80136

81137
void swift::initializeProtocolConformanceLookup() {
82-
// Search the loaded dls. This only searches the already
83-
// loaded ones.
84-
// FIXME: Find a way to have this continue to happen for dlopen-ed images.
85-
// rdar://problem/19045112
86-
InspectArgs ProtocolConformanceArgs = {
87-
ProtocolConformancesSymbol,
88-
addImageProtocolConformanceBlockCallback
89-
};
90-
dl_iterate_phdr(iteratePHDRCallback, &ProtocolConformanceArgs);
138+
initializeSectionLookup(&ProtocolConformanceArgs);
91139
}
92140

93141
void swift::initializeTypeMetadataRecordLookup() {
94-
InspectArgs TypeMetadataRecordArgs = {
95-
TypeMetadataRecordsSymbol,
96-
addImageTypeMetadataRecordBlockCallback
97-
};
98-
dl_iterate_phdr(iteratePHDRCallback, &TypeMetadataRecordArgs);
142+
initializeSectionLookup(&TypeMetadataRecordArgs);
143+
}
144+
145+
// As ELF images are loaded, ImageInspectionInit:sectionDataInit() will call
146+
// addNewDSOImage() with an address in the image that can later be used via
147+
// dladdr() to dlopen() the image after the appropiate initialize*Lookup()
148+
// function has been called.
149+
SWIFT_RUNTIME_EXPORT
150+
void swift::addNewDSOImage(const void *addr) {
151+
if (didInitializeProtocolConformanceLookup.load(std::memory_order_acquire)) {
152+
addBlockInImage(&ProtocolConformanceArgs, addr);
153+
}
154+
155+
if (didInitializeTypeMetadataLookup.load(std::memory_order_acquire)) {
156+
addBlockInImage(&TypeMetadataRecordArgs, addr);
157+
}
99158
}
100159

101160
int swift::lookupSymbol(const void *address, SymbolInfo *info) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===-- ImageInspectionInit.cpp -------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
// This file along with swift_sections.S is prepended to each shared library
14+
// on an ELF target which contains protocol and metadata sections.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#if defined(__ELF__) || defined(__ANDROID__)
19+
20+
#include "ImageInspection.h"
21+
#include <memory>
22+
23+
using namespace swift;
24+
25+
// This is called at startup and by each shared object as it is dlopen()'d to
26+
// allow the section data for the object to be loaded.
27+
__attribute__((constructor))
28+
static void sectionDataInit() {
29+
addNewDSOImage(reinterpret_cast<void *>(std::addressof(sectionDataInit)));
30+
}
31+
32+
#endif

stdlib/public/runtime/ImageInspectionStatic.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,24 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17-
#include "ImageInspection.h"
18-
#include <cstring>
19-
2017
// Currently only tested on linux but should work for any ELF platform
2118
#if defined(__ELF__) && defined(__linux__)
2219

20+
#include "ImageInspection.h"
21+
#include <cstring>
22+
2323
// These are defined in swift_sections.S to mark the start of a section with the
2424
// length of the data followed immediately by the section data
2525
struct alignas(uint64_t) Section;
2626
extern const Section protocolConformancesStart asm(".swift2_protocol_conformances_start");
2727
extern const Section typeMetadataStart asm(".swift2_type_metadata_start");
2828

29-
struct SectionInfo {
30-
uint64_t size;
31-
const char *data;
32-
};
29+
using namespace swift;
30+
31+
// Called from ImageInspectionInit
32+
void
33+
swift::addNewDSOImage(const void *addr) {
34+
}
3335

3436
static SectionInfo
3537
getSectionInfo(const Section *section) {

0 commit comments

Comments
 (0)