diff --git a/CMakeLists.txt b/CMakeLists.txt index b5c3bff8844..263b8679a1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 228707d8dd0..0d39ca82eae 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -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()); diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index f68710c2b94..a26d408643d 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -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; } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index cb073f3ca2c..a5128908b46 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -454,6 +454,27 @@ emitGlobalList(IRGenModule &IGM, ArrayRef 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(), diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 55f13726061..dda7c675fb0 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -369,6 +369,7 @@ private: ObjCProtocolPair getObjCProtocolGlobalVars(ProtocolDecl *proto); + llvm::Constant *emitProtocolConformances(); void emitGlobalLists(); void emitAutolinkInfo(); diff --git a/stdlib/runtime/CMakeLists.txt b/stdlib/runtime/CMakeLists.txt index 5c66a3de500..3ea00c49087 100644 --- a/stdlib/runtime/CMakeLists.txt +++ b/stdlib/runtime/CMakeLists.txt @@ -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() diff --git a/stdlib/runtime/Casting.cpp b/stdlib/runtime/Casting.cpp index 1b98b5276be..6ddd7dbf388 100644 --- a/stdlib/runtime/Casting.cpp +++ b/stdlib/runtime/Casting.cpp @@ -28,16 +28,19 @@ #include "../shims/RuntimeShims.h" #include "stddef.h" -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(__MACH__) #include #include -#include +#elif defined(__ELF__) +#include +#include #endif #include #include #include #include +#include #include #include @@ -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(conformances); + auto recordsEnd + = reinterpret_cast + (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(conformances); - auto recordsEnd - = reinterpret_cast - (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( + 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(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 diff --git a/stdlib/runtime/swift.ld b/stdlib/runtime/swift.ld new file mode 100644 index 00000000000..f775ec0b963 --- /dev/null +++ b/stdlib/runtime/swift.ld @@ -0,0 +1,10 @@ +SECTIONS +{ + .swift1_protocol_conformances : + { + .swift1_protocol_conformances_start = . ; + QUAD(SIZEOF(.swift1_protocol_conformances) - 8) ; + *(.swift1_protocol_conformances) ; + } +} +INSERT AFTER .dtors diff --git a/test/Driver/linker.swift b/test/Driver/linker.swift index 252a640de85..777942cbf20 100644 --- a/test/Driver/linker.swift +++ b/test/Driver/linker.swift @@ -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