[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
This commit is contained in:
Mattia Iavarone
2021-10-26 06:33:17 +02:00
committed by GitHub
parent 6a59dc7fa5
commit c92e34ca9f
14 changed files with 148 additions and 17 deletions
@@ -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
}
}
@@ -21,6 +21,8 @@ object BinaryOptions : BinaryOptionRegistry() {
val stripDebugInfoFromNativeLibs by booleanOption()
val sourceInfoType by option<SourceInfoType>()
val androidProgramType by option<AndroidProgramType>()
}
open class BinaryOption<T : Any>(
@@ -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")
}
@@ -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)
@@ -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")
}
@@ -2745,6 +2745,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionType(int32Type, false, int8TypePtr, int8TypePtr, int32Type))
overrideRuntimeGlobal("Kotlin_getSourceInfo_Function", constValue(getSourceInfoFunction!!))
}
if (context.config.target.family == Family.ANDROID && context.config.produce == CompilerOutputKind.PROGRAM) {
val configuration = context.config.configuration
val programType = configuration.get(BinaryOptions.androidProgramType) ?: AndroidProgramType.Default
overrideRuntimeGlobal("Kotlin_printToAndroidLogcat", Int32(if (programType.consolePrintsToLogcat) 1 else 0))
}
}
//-------------------------------------------------------------------------//
@@ -29,6 +29,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include "launcher.h"
#include "androidLauncher.h"
#include <android/log.h>
@@ -44,8 +45,6 @@
#endif
//--- main --------------------------------------------------------------------//
extern "C" KInt Konan_start(const ObjHeader*);
namespace {
typedef struct {
@@ -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
@@ -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
@@ -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<compiler::DestroyRuntimeMode>(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
@@ -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
@@ -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
}
+12 -4
View File
@@ -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
@@ -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