Implement ELF protocol conformance loading.

This works by loading the protocols from a specially named symbol,
which is generated by the linker through the help of a linker script
that merges all of the protocol conformance blocks into one section
with its size at the start of it and points a global symbol at
the section.

We do all this because unlike MachO, section information does not
survive to be loaded into memory with ELF binaries. Instead,
the mappings that survive are 'segments', which contain one or
more sections. Information about how these relate to their original
sections is difficult, if not impossible, to obtain at runtime.

Swift SVN r23518
This commit is contained in:
Graham Batty
2014-11-21 17:09:48 +00:00
parent 9ba13cd93a
commit 17b1721f8f
9 changed files with 160 additions and 67 deletions

View File

@@ -1161,6 +1161,13 @@ macro(add_swift_library name)
set(SWIFTLIB_DEFAULT_LINK_FLAGS
"-Xlinker -segalign -Xlinker 0x4000")
endif()
# On linux add the linker script that coalesces protocol conformance
# sections. This wouldn't be necessary if the link was done by the swift
# binary: rdar://problem/19007002
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(SWIFTLIB_DEFAULT_LINK_FLAGS
"${SWIFTLIB_DEFAULT_LINK_FLAGS} -Xlinker -Tswift.ld")
endif()
# If we're supposed to build this library for the target...
if(SWIFTLIB_TARGET_LIBRARY AND SWIFT_SDKS)

View File

@@ -669,6 +669,10 @@ Job *linux::Linker::constructJob(const JobAction &JA,
Arguments.push_back("-lswiftCore");
// Add the linker script that coalesces protocol conformance sections.
Arguments.push_back("-Xlinker");
Arguments.push_back("-Tswift.ld");
// This should be the last option, for convenience in checking output.
Arguments.push_back("-o");
Arguments.push_back(Output->getPrimaryOutputFilename().c_str());

View File

@@ -1729,7 +1729,17 @@ namespace {
? Twine("_$_") + CategoryName.str()
: Twine()));
var->setAlignment(IGM.getPointerAlignment().getValue());
var->setSection("__DATA, __objc_const");
switch (IGM.TargetInfo.OutputObjectFormat) {
case llvm::Triple::MachO:
var->setSection("__DATA, __objc_const");
break;
case llvm::Triple::ELF:
var->setSection(".data");
break;
default:
llvm_unreachable("Don't know how to emit private global constants for "
"the selected object format.");
}
return var;
}

View File

@@ -454,6 +454,27 @@ emitGlobalList(IRGenModule &IGM, ArrayRef<llvm::WeakVH> handles,
return var;
}
/// Emit the protocol conformance list and return it
llvm::Constant *IRGenModule::emitProtocolConformances() {
std::string sectionName;
switch (TargetInfo.OutputObjectFormat) {
case llvm::Triple::MachO:
sectionName = "__DATA, __swift1_proto, regular, no_dead_strip";
break;
case llvm::Triple::ELF:
sectionName = ".swift1_protocol_conformances";
break;
default:
llvm_unreachable("Don't know how to emit protocol conformances for "
"the selected object format.");
}
return emitGlobalList(*this, ProtocolConformanceRecords,
"protocol_conformances", sectionName,
llvm::GlobalValue::InternalLinkage,
ProtocolConformanceRecordTy, true);
}
void IRGenModule::emitRuntimeRegistration() {
// Duck out early if we have nothing to register.
if (ProtocolConformanceRecords.empty()
@@ -558,13 +579,9 @@ void IRGenModule::emitRuntimeRegistration() {
}
// Register Swift protocol conformances if we added any.
if (!ProtocolConformanceRecords.empty()) {
llvm::Constant *conformances
= emitGlobalList(*this, ProtocolConformanceRecords,
"protocol_conformances",
"__DATA, __swift1_proto, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
ProtocolConformanceRecordTy,
true);
llvm::Constant *conformances = emitProtocolConformances();
llvm::Constant *beginIndices[] = {
llvm::ConstantInt::get(Int32Ty, 0),
llvm::ConstantInt::get(Int32Ty, 0),
@@ -602,37 +619,37 @@ void IRGenModule::addProtocolConformanceRecord(llvm::Constant *conformanceRec) {
}
void IRGenModule::emitGlobalLists() {
// Objective-C class references go in a variable with a meaningless
// name but a magic section.
emitGlobalList(*this, ObjCClasses, "objc_classes",
"__DATA, __objc_classlist, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
Int8PtrTy,
false);
// So do categories.
emitGlobalList(*this, ObjCCategories, "objc_categories",
"__DATA, __objc_catlist, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
Int8PtrTy,
false);
if (ObjCInterop) {
assert(TargetInfo.OutputObjectFormat == llvm::Triple::MachO);
// Objective-C class references go in a variable with a meaningless
// name but a magic section.
emitGlobalList(*this, ObjCClasses, "objc_classes",
"__DATA, __objc_classlist, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
Int8PtrTy,
false);
// So do categories.
emitGlobalList(*this, ObjCCategories, "objc_categories",
"__DATA, __objc_catlist, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
Int8PtrTy,
false);
// Emit nonlazily realized class references in a second magic section to make
// sure they are realized by the Objective-C runtime before any instances
// are allocated.
emitGlobalList(*this, ObjCNonLazyClasses, "objc_non_lazy_classes",
"__DATA, __objc_nlclslist, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
Int8PtrTy,
false);
// Emit nonlazily realized class references in a second magic section to make
// sure they are realized by the Objective-C runtime before any instances
// are allocated.
emitGlobalList(*this, ObjCNonLazyClasses, "objc_non_lazy_classes",
"__DATA, __objc_nlclslist, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
Int8PtrTy,
false);
}
// Emit protocol conformances into a section we can recognize at runtime.
// In JIT mode we need to manually register these conformances later.
if (!Opts.UseJIT)
emitGlobalList(*this, ProtocolConformanceRecords, "protocol_conformances",
"__DATA, __swift1_proto, regular, no_dead_strip",
llvm::GlobalValue::InternalLinkage,
ProtocolConformanceRecordTy,
true);
if (!Opts.UseJIT) {
emitProtocolConformances();
}
// @llvm.used
assert(std::all_of(LLVMUsed.begin(), LLVMUsed.end(),

View File

@@ -369,6 +369,7 @@ private:
ObjCProtocolPair getObjCProtocolGlobalVars(ProtocolDecl *proto);
llvm::Constant *emitProtocolConformances();
void emitGlobalLists();
void emitAutolinkInfo();

View File

@@ -47,6 +47,10 @@ add_swift_library(swiftRuntime
FRAMEWORK_DEPENDS ${FRAMEWORKS})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# FIXME: This is probably not flexible enough to deal with 32bit builds,
# but SWIFTLIB_DIR isn't available in this cmake file and duplicating
# the logic seems wrong. rdar://problem/19035586
configure_file(swift.ld ${CMAKE_BINARY_DIR}/lib/swift/swift.ld COPYONLY)
target_link_libraries(swiftRuntime "-lpthread" "-ldl")
endif()

View File

@@ -28,16 +28,19 @@
#include "../shims/RuntimeShims.h"
#include "stddef.h"
#ifdef __APPLE__
#if defined(__APPLE__) && defined(__MACH__)
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
#include <dispatch/dispatch.h>
#elif defined(__ELF__)
#include <elf.h>
#include <link.h>
#endif
#include <dlfcn.h>
#include <cstring>
#include <deque>
#include <mutex>
#include <atomic>
#include <sstream>
#include <type_traits>
@@ -1656,14 +1659,17 @@ const {
}
}
// TODO: Implement protocol conformance lookup for non-Apple environments
#ifdef __APPLE__
#if defined(__APPLE__) && defined(__MACH__)
#define SWIFT_PROTOCOL_CONFORMANCES_SECTION "__swift1_proto"
#elif defined(__ELF__)
#define SWIFT_PROTOCOL_CONFORMANCES_SECTION ".swift1_protocol_conformances_start"
#endif
// dispatch_once token to install the dyld callback to enqueue images for
// FIXME: Implement this callback on non-apple platforms.
// std:once_flag token to install the dyld callback to enqueue images for
// protocol conformance lookup.
static dispatch_once_t InstallProtocolConformanceAddImageCallbackOnce = 0;
static std::once_flag InstallProtocolConformanceAddImageCallbackOnce;
// Monotonic generation number that is increased when we load an image with
// new protocol conformances.
@@ -1811,6 +1817,21 @@ swift::swift_registerProtocolConformances(const ProtocolConformanceRecord *begin
pthread_mutex_unlock(&SectionsToScanLock);
}
static void _addImageProtocolConformancesBlock(const uint8_t *conformances,
size_t conformancesSize) {
assert(conformancesSize % sizeof(ProtocolConformanceRecord) == 0
&& "weird-sized conformances section?!");
// If we have a section, enqueue the conformances for lookup.
auto recordsBegin
= reinterpret_cast<const ProtocolConformanceRecord*>(conformances);
auto recordsEnd
= reinterpret_cast<const ProtocolConformanceRecord*>
(conformances + conformancesSize);
swift_registerProtocolConformances(recordsBegin, recordsEnd);
}
#if defined(__APPLE__) && defined(__MACH__)
static void _addImageProtocolConformances(const mach_header *mh,
intptr_t vmaddr_slide) {
#ifdef __LP64__
@@ -1830,29 +1851,57 @@ static void _addImageProtocolConformances(const mach_header *mh,
if (!conformances)
return;
assert(conformancesSize % sizeof(ProtocolConformanceRecord) == 0
&& "weird-sized conformances section?!");
// If we have a section, enqueue the conformances for lookup.
auto recordsBegin
= reinterpret_cast<const ProtocolConformanceRecord*>(conformances);
auto recordsEnd
= reinterpret_cast<const ProtocolConformanceRecord*>
(conformances + conformancesSize);
swift_registerProtocolConformances(recordsBegin, recordsEnd);
_addImageProtocolConformancesBlock(conformances, conformancesSize);
}
#elif defined(__ELF__)
static int _addImageProtocolConformances(struct dl_phdr_info *info,
size_t size, void * /*data*/) {
// Skip the executable and ld-linux.so, which both have a null or empty name.
if (!info->dlpi_name || info->dlpi_name[0] == '\0')
return 0;
void *handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
auto conformances = reinterpret_cast<const uint8_t*>(
dlsym(handle, SWIFT_PROTOCOL_CONFORMANCES_SECTION));
if (!conformances) {
// if there are no conformances, don't hold this handle open.
dlclose(handle);
return 0;
}
// Extract the size of the conformances block from the head of the section
auto conformancesSize = *reinterpret_cast<const uint64_t*>(conformances);
conformances += sizeof(conformancesSize);
_addImageProtocolConformancesBlock(conformances, conformancesSize);
dlclose(handle);
return 0;
}
#endif
const WitnessTable *swift::swift_conformsToProtocol(const Metadata *type,
const ProtocolDescriptor *protocol){
// TODO: Generic types, subclasses, foreign classes
// Install our dyld callback if we haven't already.
// Dyld will invoke this on our behalf for all images that have already been
// loaded.
dispatch_once(&InstallProtocolConformanceAddImageCallbackOnce, ^(void){
std::call_once(InstallProtocolConformanceAddImageCallbackOnce, [](){
#if defined(__APPLE__) && defined(__MACH__)
// Install our dyld callback if we haven't already.
// Dyld will invoke this on our behalf for all images that have already been
// loaded.
_dyld_register_func_for_add_image(_addImageProtocolConformances);
#elif defined(__ELF__)
// Search the loaded dls. Unlike the above, this only searches the already
// loaded ones.
// FIXME: Find a way to have this continue to happen after.
// rdar://problem/19045112
dl_iterate_phdr(_addImageProtocolConformances, nullptr);
#else
#error No known mechanism to inspect loaded dynamic libraries on this platform.
#endif
});
auto origType = type;
recur:
@@ -1969,16 +2018,6 @@ recur_inside_cache_lock:
goto recur;
}
#else // if !__APPLE__
const WitnessTable *swift::swift_conformsToProtocol(const Metadata *type,
const ProtocolDescriptor *protocol){
// Not implemented for non-Apple platforms.
return nullptr;
}
#endif // __APPLE__
// The return type is incorrect. It is only important that it is
// passed using 'sret'.
extern "C" OpaqueExistentialContainer

10
stdlib/runtime/swift.ld Normal file
View File

@@ -0,0 +1,10 @@
SECTIONS
{
.swift1_protocol_conformances :
{
.swift1_protocol_conformances_start = . ;
QUAD(SIZEOF(.swift1_protocol_conformances) - 8) ;
*(.swift1_protocol_conformances) ;
}
}
INSERT AFTER .dtors

View File

@@ -63,6 +63,7 @@
// LINUX-DAG: -lswiftCore
// LINUX-DAG: -L [[STDLIB_PATH:[^ ]+/lib/swift]]
// LINUX-DAG: -Xlinker -rpath -Xlinker [[STDLIB_PATH]]
// LINUX-DAG: -Xlinker -Tswift.ld
// LINUX-DAG: -F foo
// LINUX-DAG: -framework bar
// LINUX-DAG: -L baz