From c92e34ca9fe5f175442c95eed935bbade5b63d90 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Tue, 26 Oct 2021 06:33:17 +0200 Subject: [PATCH] [Native] Enable real Android Native executables (#4624) * Add AndroidProgramType binary option, override entry point, fix linker flags * Add Konan_main_standalone entry point to the Android runtime * Add compiler warning * Make standalone programs print to stdout * Fix warning message and readability --- .../backend/konan/AndroidProgramType.kt | 31 +++++++++++++++++++ .../kotlin/backend/konan/BinaryOptions.kt | 2 ++ .../kotlin/backend/konan/CompilerOutput.kt | 5 +-- .../kotlin/backend/konan/KonanConfig.kt | 23 ++++++++++++++ .../jetbrains/kotlin/backend/konan/Linker.kt | 11 ++++++- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 5 +++ .../src/launcher/cpp/androidLauncher.cpp | 3 +- .../runtime/src/launcher/cpp/launcher.cpp | 10 +++--- .../runtime/src/launcher/cpp/launcher.h | 30 ++++++++++++++++++ .../src/main/cpp/CompilerConstants.cpp | 9 ++++++ .../src/main/cpp/CompilerConstants.hpp | 4 +++ .../runtime/src/main/cpp/Console.cpp | 9 +++++- .../runtime/src/main/cpp/Porting.cpp | 16 +++++++--- .../jetbrains/kotlin/konan/target/Linker.kt | 7 +++-- 14 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/AndroidProgramType.kt create mode 100644 kotlin-native/runtime/src/launcher/cpp/launcher.h diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/AndroidProgramType.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/AndroidProgramType.kt new file mode 100644 index 00000000000..47ca4e67646 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/AndroidProgramType.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.konan.target.LinkerOutputKind + +/** + * For historical reasons, Android Native executables can be of two types: + * - [Standalone] is a regular executable, in which the Konan entry point receives command line arguments as usual. + * - [NativeActivity] is a shared library in which the Konan entry point matches Android NDK's NativeActivity + * signature ( https://developer.android.com/ndk/reference/group/native-activity#anativeactivity_createfunc ). + */ +enum class AndroidProgramType( + val konanMainOverride: String?, + val linkerOutputKindOverride: LinkerOutputKind?, + val consolePrintsToLogcat: Boolean +) { + + /** Regular executable. The runtime entry point name is not Konan_main for historical reasons. */ + Standalone("Konan_main_standalone", null, false), + + /** Native activity "executable" - a shared library with a specific entry point. */ + NativeActivity(null, LinkerOutputKind.DYNAMIC_LIBRARY, true); + + companion object { + val Default = NativeActivity + } +} \ No newline at end of file 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 6aa0d9dfce9..0994241c830 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 @@ -21,6 +21,8 @@ object BinaryOptions : BinaryOptionRegistry() { val stripDebugInfoFromNativeLibs by booleanOption() val sourceInfoType by option() + + val androidProgramType by option() } open class BinaryOption( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index b26109d3533..9ff37a999fa 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -85,8 +85,9 @@ private fun insertAliasToEntryPoint(context: Context) { if (context.config.produce != CompilerOutputKind.PROGRAM || nomain) return val module = context.llvmModule - val entryPoint = LLVMGetNamedFunction(module, "Konan_main") - ?: error("Module doesn't contain `Konan_main`") + val entryPointName = context.config.entryPointName + val entryPoint = LLVMGetNamedFunction(module, entryPointName) + ?: error("Module doesn't contain `$entryPointName`") LLVMAddAlias(module, LLVMTypeOf(entryPoint)!!, entryPoint, "main") } 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 a23569e9238..5b5dbf17be7 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 @@ -275,6 +275,29 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration internal val propertyLazyInitialization: Boolean get() = configuration.get(KonanConfigKeys.PROPERTY_LAZY_INITIALIZATION)!! internal val lazyIrForCaches: Boolean get() = configuration.get(KonanConfigKeys.LAZY_IR_FOR_CACHES)!! + + internal val entryPointName: String by lazy { + if (target.family == Family.ANDROID) { + val androidProgramTypeOrNull = configuration.get(BinaryOptions.androidProgramType) + if (androidProgramTypeOrNull == null) { + configuration.report(CompilerMessageSeverity.WARNING, """ + Android Native executables are currently built as shared libraries with NativeActivity support, but the default behavior is going to change in 1.7.0 to build regular executables instead. + To keep using NativeActivity support, add binaryOptions["androidProgramType"] = "nativeActivity" to your androidNative executable configuration block in Gradle script: + binaries { + executable { + binaryOptions["androidProgramType"] = "nativeActivity" + } + } + See https://youtrack.jetbrains.com/issue/KT-49406 for more details. + """.trimIndent()) + } + val androidProgramType = androidProgramTypeOrNull ?: AndroidProgramType.Default + if (androidProgramType.konanMainOverride != null) { + return@lazy androidProgramType.konanMainOverride + } + } + "Konan_main" + } } fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String) 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 a6b2a8c1b55..27d9d396322 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 @@ -21,7 +21,16 @@ internal fun determineLinkerOutput(context: Context): LinkerOutputKind = CompilerOutputKind.DYNAMIC -> LinkerOutputKind.DYNAMIC_LIBRARY CompilerOutputKind.STATIC_CACHE, CompilerOutputKind.STATIC -> LinkerOutputKind.STATIC_LIBRARY - CompilerOutputKind.PROGRAM -> LinkerOutputKind.EXECUTABLE + CompilerOutputKind.PROGRAM -> run { + if (context.config.target.family == Family.ANDROID) { + val configuration = context.config.configuration + val androidProgramType = configuration.get(BinaryOptions.androidProgramType) ?: AndroidProgramType.Default + if (androidProgramType.linkerOutputKindOverride != null) { + return@run androidProgramType.linkerOutputKindOverride + } + } + LinkerOutputKind.EXECUTABLE + } else -> TODO("${context.config.produce} should not reach native linker stage") } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 98356767689..d64a7f764be 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -2745,6 +2745,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map #include +#include "launcher.h" #include "androidLauncher.h" #include @@ -44,8 +45,6 @@ #endif //--- main --------------------------------------------------------------------// -extern "C" KInt Konan_start(const ObjHeader*); - namespace { typedef struct { diff --git a/kotlin-native/runtime/src/launcher/cpp/launcher.cpp b/kotlin-native/runtime/src/launcher/cpp/launcher.cpp index b037aef88ab..0bf6414d546 100644 --- a/kotlin-native/runtime/src/launcher/cpp/launcher.cpp +++ b/kotlin-native/runtime/src/launcher/cpp/launcher.cpp @@ -21,7 +21,7 @@ #include "Types.h" #include "Worker.h" -#ifndef KONAN_ANDROID +#include "launcher.h" //--- Setup args --------------------------------------------------------------// @@ -38,8 +38,6 @@ OBJ_GETTER(setupArgs, int argc, const char** argv) { } //--- main --------------------------------------------------------------------// -extern "C" KInt Konan_start(const ObjHeader*); - extern "C" KInt Konan_run_start(int argc, const char** argv) { ObjHolder args; setupArgs(argc, argv, args.slot()); @@ -64,7 +62,11 @@ extern "C" RUNTIME_USED int Init_and_run_start(int argc, const char** argv, int return exitStatus; } +#ifndef KONAN_ANDROID extern "C" RUNTIME_USED int Konan_main(int argc, const char** argv) { +#else +extern "C" RUNTIME_USED int Konan_main_standalone(int argc, const char** argv) { +#endif return Init_and_run_start(argc, argv, 1); } @@ -83,6 +85,4 @@ extern "C" RUNTIME_USED int Konan_js_main(int argc, int memoryDeInit) { return Init_and_run_start(argc, (const char**)argv, memoryDeInit); } -#endif - #endif diff --git a/kotlin-native/runtime/src/launcher/cpp/launcher.h b/kotlin-native/runtime/src/launcher/cpp/launcher.h new file mode 100644 index 00000000000..486e219dae0 --- /dev/null +++ b/kotlin-native/runtime/src/launcher/cpp/launcher.h @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LAUNCHER_H +#define LAUNCHER_H + +#ifdef __cplusplus +extern "C" { +#endif + +KInt Konan_start(const ObjHeader*); + +#ifdef __cplusplus +} +#endif + +#endif // LAUNCHER_H \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp index 518615e9b97..8fb1c3ad1a9 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp @@ -16,6 +16,9 @@ RUNTIME_WEAK int32_t Kotlin_gcAggressive = 0; RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0; RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1; RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr; +#ifdef KONAN_ANDROID +RUNTIME_WEAK int32_t Kotlin_printToAndroidLogcat = 1; +#endif ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept { return static_cast(Kotlin_destroyRuntimeMode); @@ -32,3 +35,9 @@ ALWAYS_INLINE compiler::WorkerExceptionHandling compiler::workerExceptionHandlin ALWAYS_INLINE bool compiler::freezingEnabled() noexcept { return Kotlin_freezingEnabled != 0; } + +#ifdef KONAN_ANDROID +ALWAYS_INLINE bool compiler::printToAndroidLogcat() noexcept { + return Kotlin_printToAndroidLogcat != 0; +} +#endif diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp index fbf55c56509..02868cf777a 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp @@ -82,6 +82,10 @@ ALWAYS_INLINE inline int getSourceInfo(void* addr, SourceInfo *result, int resul } } +#ifdef KONAN_ANDROID +bool printToAndroidLogcat() noexcept; +#endif + } // namespace compiler } // namespace kotlin diff --git a/kotlin-native/runtime/src/main/cpp/Console.cpp b/kotlin-native/runtime/src/main/cpp/Console.cpp index c7ca373c339..8e1ace0de8c 100644 --- a/kotlin-native/runtime/src/main/cpp/Console.cpp +++ b/kotlin-native/runtime/src/main/cpp/Console.cpp @@ -22,6 +22,9 @@ #include "Porting.h" #include "Types.h" #include "Exceptions.h" +#ifdef KONAN_ANDROID +#include "CompilerConstants.hpp" +#endif #include "utf8.h" @@ -46,8 +49,12 @@ void Kotlin_io_Console_print(KString message) { void Kotlin_io_Console_println(KString message) { Kotlin_io_Console_print(message); #ifndef KONAN_ANDROID - // On Android single print produces logcat entry, so no need in linefeed. Kotlin_io_Console_println0(); +#else + // On Android single print produces logcat entry, so no need in linefeed. + if (!kotlin::compiler::printToAndroidLogcat()) { + Kotlin_io_Console_println0(); + } #endif } diff --git a/kotlin-native/runtime/src/main/cpp/Porting.cpp b/kotlin-native/runtime/src/main/cpp/Porting.cpp index 73d3ab1821e..e6480310555 100644 --- a/kotlin-native/runtime/src/main/cpp/Porting.cpp +++ b/kotlin-native/runtime/src/main/cpp/Porting.cpp @@ -62,8 +62,12 @@ void consoleInit() { void consoleWriteUtf8(const char* utf8, uint32_t sizeBytes) { #ifdef KONAN_ANDROID - // TODO: use sizeBytes! - __android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8); + if (kotlin::compiler::printToAndroidLogcat()) { + // TODO: use sizeBytes! + __android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8); + } else { + ::write(STDOUT_FILENO, utf8, sizeBytes); + } #else ::write(STDOUT_FILENO, utf8, sizeBytes); #endif @@ -71,8 +75,12 @@ void consoleWriteUtf8(const char* utf8, uint32_t sizeBytes) { NO_EXTERNAL_CALLS_CHECK void consoleErrorUtf8(const char* utf8, uint32_t sizeBytes) { #ifdef KONAN_ANDROID - // TODO: use sizeBytes! - __android_log_print(ANDROID_LOG_ERROR, "Konan_main", "%s", utf8); + if (kotlin::compiler::printToAndroidLogcat()) { + // TODO: use sizeBytes! + __android_log_print(ANDROID_LOG_ERROR, "Konan_main", "%s", utf8); + } else { + ::write(STDERR_FILENO, utf8, sizeBytes); + } #else ::write(STDERR_FILENO, utf8, sizeBytes); #endif diff --git a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt index c6e2fc7574c..65ad34c2e23 100644 --- a/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt +++ b/kotlin-native/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt @@ -145,8 +145,11 @@ class AndroidLinker(targetProperties: AndroidConfigurables) return listOf(Command(clang).apply { +"-o" +executable - +"-fPIC" - +"-shared" + when (kind) { + LinkerOutputKind.EXECUTABLE -> +listOf("-fPIE", "-pie") + LinkerOutputKind.DYNAMIC_LIBRARY -> +listOf("-fPIC", "-shared") + LinkerOutputKind.STATIC_LIBRARY -> {} + } +"-target" +clangTarget +libDirs