From 51651aef748950036f719d02cfdb2789d1930157 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Mon, 25 Jul 2022 19:15:05 +0200 Subject: [PATCH] [K/N] Basic support of thread sanitizer for generated code --- .../kotlin/backend/konan/BinaryOptions.kt | 3 ++ .../kotlin/backend/konan/KonanConfig.kt | 18 ++++++++++- .../jetbrains/kotlin/backend/konan/Linker.kt | 4 ++- .../backend/konan/OptimizationPipeline.kt | 16 +++++++++- .../backend/konan/llvm/LlvmAttributes.kt | 1 + .../kotlin/backend/konan/llvm/LlvmUtils.kt | 30 +++++-------------- .../src/main/cpp/CAPIExtensions.cpp | 5 ++++ .../src/main/include/CAPIExtensions.h | 2 ++ 8 files changed, 54 insertions(+), 25 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt index e890153c5bd..ceee203ab64 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.config.CompilerConfigurationKey +import org.jetbrains.kotlin.konan.target.SanitizerKind import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadOnlyProperty @@ -37,6 +38,8 @@ object BinaryOptions : BinaryOptionRegistry() { val bundleVersion by stringOption() val appStateTracking by option() + + val sanitizer by option() } open class BinaryOption( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index af4613a08ba..9b514c86f34 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -60,6 +60,19 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration ?: target.family.isAppleFamily // Default is true for Apple targets. val generateDebugTrampoline = debug && configuration.get(KonanConfigKeys.GENERATE_DEBUG_TRAMPOLINE) ?: false val optimizationsEnabled = configuration.getBoolean(KonanConfigKeys.OPTIMIZATION) + val sanitizer = configuration.get(BinaryOptions.sanitizer)?.takeIf { + when { + it != SanitizerKind.THREAD -> "${it.name} sanitizer is not supported yet" + produce == CompilerOutputKind.STATIC -> "${it.name} sanitizer is unsupported for static library" + produce == CompilerOutputKind.FRAMEWORK && produceStaticFramework -> "${it.name} sanitizer is unsupported for static framework" + it !in target.supportedSanitizers() -> "${it.name} sanitizer is unsupported on ${target.name}" + else -> null + }?.let {message -> + configuration.report(CompilerMessageSeverity.STRONG_WARNING, message) + return@takeIf false + } + return@takeIf true + } private val defaultMemoryModel get() = if (target.supportsThreads()) { @@ -226,7 +239,9 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration private val shouldCoverLibraries = !configuration.getList(KonanConfigKeys.LIBRARIES_TO_COVER).isNullOrEmpty() private val defaultAllocationMode get() = when { - memoryModel == MemoryModel.EXPERIMENTAL && target.supportsMimallocAllocator() -> AllocationMode.MIMALLOC + memoryModel == MemoryModel.EXPERIMENTAL && target.supportsMimallocAllocator() && sanitizer == null -> { + AllocationMode.MIMALLOC + } else -> AllocationMode.STD } @@ -364,6 +379,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration } freezing != defaultFreezing -> "with ${freezing.name.replaceFirstChar { it.lowercase() }} freezing mode" runtimeAssertsMode != RuntimeAssertsMode.IGNORE -> "with runtime assertions" + sanitizer != null -> "with sanitizers enabled" else -> null } CacheSupport( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt index b261255fa17..61838fc8ceb 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt @@ -180,7 +180,9 @@ internal class Linker(val context: Context) { kind = linkerOutput, outputDsymBundle = context.config.outputFiles.symbolicInfoFile, needsProfileLibrary = needsProfileLibrary, - mimallocEnabled = mimallocEnabled) + mimallocEnabled = mimallocEnabled, + sanitizer = context.config.sanitizer + ) (linkerInput.preLinkCommands + finalOutputCommands).forEach { it.logWith(context::log) it.execute() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt index df74c905b06..5b3a66fc488 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt @@ -53,6 +53,7 @@ data class LlvmPipelineConfig( val makeDeclarationsHidden: Boolean, val objCPasses: Boolean, val inlineThreshold: Int?, + val sanitizer: SanitizerKind?, ) private fun getCpuModel(context: Context): String { @@ -100,7 +101,8 @@ internal fun createLTOPipelineConfigForRuntime(context: Context): LlvmPipelineCo internalize = false, objCPasses = configurables is AppleConfigurables, makeDeclarationsHidden = false, - inlineThreshold = tryGetInlineThreshold(context) + inlineThreshold = tryGetInlineThreshold(context), + sanitizer = null ) } @@ -171,6 +173,7 @@ internal fun createLTOFinalPipelineConfig(context: Context): LlvmPipelineConfig makeDeclarationsHidden, objcPasses, inlineThreshold, + context.config.sanitizer ) } @@ -238,6 +241,17 @@ class LlvmOptimizationPipeline( // TODO: Consider adding other ObjC passes. LLVMAddObjCARCContractPass(modulePasses) } + + when (config.sanitizer) { + null -> {} + SanitizerKind.ADDRESS -> TODO("Address sanitizer is not supported yet") + SanitizerKind.THREAD -> { + getFunctions(llvmModule) + .filter { LLVMIsDeclaration(it) == 0 } + .forEach { addLlvmFunctionEnumAttribute(it, LlvmFunctionAttribute.SanitizeThread) } + LLVMAddThreadSanitizerPass(modulePasses) + } + } } fun run() { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt index c81f2ca38fb..16f023680b5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt @@ -113,4 +113,5 @@ sealed class LlvmFunctionAttribute(private val llvmAttributeName: String) : Llvm object NoUnwind : LlvmFunctionAttribute("nounwind") object NoReturn : LlvmFunctionAttribute("noreturn") object NoInline : LlvmFunctionAttribute("noinline") + object SanitizeThread : LlvmFunctionAttribute("sanitize_thread") } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index 2c085139cc1..51e97e7a0ed 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -359,25 +359,8 @@ fun parseBitcodeFile(path: String): LLVMModuleRef = memScoped { } } -private val nounwindAttrKindId by lazy { - getLlvmAttributeKindId("nounwind") -} - -private val noreturnAttrKindId by lazy { - getLlvmAttributeKindId("noreturn") -} - -private val noinlineAttrKindId by lazy { - getLlvmAttributeKindId("noinline") -} - -private val signextAttrKindId by lazy { - getLlvmAttributeKindId("signext") -} - - fun isFunctionNoUnwind(function: LLVMValueRef): Boolean { - val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, nounwindAttrKindId.value) + val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, LlvmFunctionAttribute.NoUnwind.asAttributeKindId().value) return attribute != null } @@ -392,15 +375,15 @@ internal fun getLlvmAttributeKindId(attributeName: String): LLVMAttributeKindId data class LLVMAttributeKindId(val value: Int) fun setFunctionNoUnwind(function: LLVMValueRef) { - addLlvmFunctionEnumAttribute(function, nounwindAttrKindId) + addLlvmFunctionEnumAttribute(function, LlvmFunctionAttribute.NoUnwind) } fun setFunctionNoReturn(function: LLVMValueRef) { - addLlvmFunctionEnumAttribute(function, noreturnAttrKindId) + addLlvmFunctionEnumAttribute(function, LlvmFunctionAttribute.NoReturn) } fun setFunctionNoInline(function: LLVMValueRef) { - addLlvmFunctionEnumAttribute(function, noinlineAttrKindId) + addLlvmFunctionEnumAttribute(function, LlvmFunctionAttribute.NoInline) } internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attrKindId: LLVMAttributeKindId, value: Long = 0) { @@ -408,6 +391,9 @@ internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attrKindId: LL addLlvmFunctionAttribute(function, attribute) } +internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attr: LlvmAttribute, value: Long = 0) = + addLlvmFunctionEnumAttribute(function, attr.asAttributeKindId(), value) + internal fun createLlvmEnumAttribute(llvmContext: LLVMContextRef, attrKindId: LLVMAttributeKindId, value: Long = 0) = LLVMCreateEnumAttribute(llvmContext, attrKindId.value, value)!! @@ -417,7 +403,7 @@ internal fun addLlvmFunctionAttribute(function: LLVMValueRef, attribute: LLVMAtt fun addFunctionSignext(function: LLVMValueRef, index: Int, type: LLVMTypeRef?) { if (type == int1Type || type == int8Type || type == int16Type) { - val attribute = createLlvmEnumAttribute(LLVMGetTypeContext(function.type)!!, signextAttrKindId) + val attribute = createLlvmEnumAttribute(LLVMGetTypeContext(function.type)!!, LlvmParameterAttribute.SignExt.asAttributeKindId()) LLVMAddAttributeAtIndex(function, index, attribute) } } diff --git a/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp b/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp index 92ac4bed4e5..3eaad2291e9 100644 --- a/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp +++ b/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using namespace llvm; @@ -52,4 +53,8 @@ void LLVMSetNoTailCall(LLVMValueRef Call) { int LLVMInlineCall(LLVMValueRef call) { InlineFunctionInfo IFI; return InlineFunction(*unwrap(call), IFI).isSuccess(); +} + +void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM) { + unwrap(PM)->add(createThreadSanitizerLegacyPassPass()); } \ No newline at end of file diff --git a/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h b/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h index d939d35f0ae..c9d5f654804 100644 --- a/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h +++ b/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h @@ -25,6 +25,8 @@ void LLVMSetNoTailCall(LLVMValueRef Call); int LLVMInlineCall(LLVMValueRef call); +void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM); + # ifdef __cplusplus } # endif \ No newline at end of file