From 9d211e74c514f396191d2dac83e38685b74077a2 Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Wed, 13 Dec 2017 13:40:30 +0300 Subject: [PATCH] [stage][link] commmand reprisentation --- .../kotlin/backend/konan/LinkStage.kt | 250 ++++++++++-------- 1 file changed, 133 insertions(+), 117 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 81ac0139fef..428136737db 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.backend.konan +import org.jetbrains.kotlin.konan.exec.runTool import java.lang.ProcessBuilder import java.lang.ProcessBuilder.Redirect import org.jetbrains.kotlin.konan.file.* @@ -50,7 +51,7 @@ internal abstract class PlatformFlags(val properties: KonanProperties) { open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor. abstract fun linkCommand(objectFiles: List, - executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): List + executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command open fun linkCommandSuffix(): List = emptyList() @@ -69,6 +70,25 @@ internal abstract class PlatformFlags(val properties: KonanProperties) { } +internal open class Command(tool:String) { + private val opts = mutableListOf(tool) + val libs = mutableListOf() + operator fun String.unaryPlus():Command { + opts += this + return this@Command + } + + operator fun List.unaryPlus():Command { + opts.addAll(this) + return this@Command + } + + open fun execute() = runTool(*opts.toTypedArray()) + + fun externalLibraries(deps: List) { + libs.addAll(deps) + } +} internal open class AndroidPlatform(distribution: Distribution) : PlatformFlags(distribution.targetProperties) { @@ -81,19 +101,19 @@ internal open class AndroidPlatform(distribution: Distribution) override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isUnixStaticLib } - override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): List { + override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { // liblog.so must be linked in, as we use its functionality in runtime. - return mutableListOf(clang).apply{ - add("-o") - add(executable) - add("-fPIC") - add("-shared") - add("-llog") - addAll(objectFiles) - if (optimize) addAll(linkerOptimizationFlags) - if (!debug) addAll(linkerNoDebugFlags) - if (dynamic) addAll(linkerDynamicFlags) - addAll(linkerKonanFlags) + return Command(clang).apply { + + "-o" + + executable + + "-fPIC" + + "-shared" + + "-llog" + + objectFiles + if (optimize) + linkerOptimizationFlags + if (!debug) + linkerNoDebugFlags + if (dynamic) + linkerDynamicFlags + + linkerKonanFlags } } } @@ -115,19 +135,24 @@ internal open class MacOSBasedPlatform(distribution: Distribution) override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isUnixStaticLib } - override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): List { - return mutableListOf(linker).apply { - add("-demangle") - addAll(listOf("-object_path_lto", "temporary.o", "-lto_library", libLTO)) - addAll(listOf("-dynamic", "-arch", propertyTargetString("arch"))) - addAll(osVersionMin) - addAll(listOf("-syslibroot", targetSysRoot, "-o", executable)) - addAll(objectFiles) - if (optimize) addAll(linkerOptimizationFlags) - if (!debug) addAll(linkerNoDebugFlags) - if (dynamic) addAll(linkerDynamicFlags) - addAll(linkerKonanFlags) - add("-lSystem") + override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { + return object : Command(linker){ + override fun execute() { + super.execute() + runTool(*dsymutilCommand(executable).toTypedArray()) + } + }.apply { + + "-demangle" + + listOf("-object_path_lto", "temporary.o", "-lto_library", libLTO) + + listOf("-dynamic", "-arch", propertyTargetString("arch")) + + osVersionMin + + listOf("-syslibroot", targetSysRoot, "-o", executable) + + objectFiles + if (optimize) + linkerOptimizationFlags + if (!debug) + linkerNoDebugFlags + if (dynamic) + linkerDynamicFlags + + linkerKonanFlags + + "-lSystem" } } @@ -136,6 +161,7 @@ internal open class MacOSBasedPlatform(distribution: Distribution) open fun dsymutilDryRunVerboseCommand(executable: ExecutableFile): List = listOf(dsymutil, "-dump-debug-map" ,executable) } + internal open class LinuxBasedPlatform(val distribution: Distribution) : PlatformFlags(distribution.targetProperties) { @@ -149,47 +175,52 @@ internal open class LinuxBasedPlatform(val distribution: Distribution) override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isUnixStaticLib } - override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): List { + override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { val isMips = (distribution.target == KonanTarget.LINUX_MIPS32 || - distribution.target == KonanTarget.LINUX_MIPSEL32) + distribution.target == KonanTarget.LINUX_MIPSEL32) // TODO: Can we extract more to the konan.properties? - return listConstructor( - linker, - "--sysroot=${targetSysRoot}", - "-export-dynamic", "-z", "relro", - "--build-id", "--eh-frame-hdr", // "-m", "elf_x86_64", - "-dynamic-linker", propertyTargetString("dynamicLinker"), - "-o", executable, - If (!dynamic, - "${targetSysRoot}/usr/lib64/crt1.o"), - "${targetSysRoot}/usr/lib64/crti.o", + return Command(linker).apply { + + "--sysroot=${targetSysRoot}" + + "-export-dynamic" + + "-z" + + "relro" + + "--build-id" + + "--eh-frame-hdr" + // + "-m" + // + "elf_x86_64", + + "-dynamic-linker" + + propertyTargetString("dynamicLinker") + + "-o" + + executable + if (!dynamic) + "$targetSysRoot/usr/lib64/crt1.o" + + "$targetSysRoot/usr/lib64/crti.o" if (dynamic) - "${libGcc}/crtbeginS.o" + + "$libGcc/crtbeginS.o" else - "${libGcc}/crtbegin.o", - "-L${llvmLib}", "-L${libGcc}", - If (!isMips, // MIPS doesn't support hash-style=gnu - "--hash-style=gnu"), - specificLibs, - "-L${targetSysRoot}/../lib", "-L${targetSysRoot}/lib", "-L${targetSysRoot}/usr/lib", - If (optimize, - "-plugin", "$llvmLib/LLVMgold.so", - pluginOptimizationFlags, - linkerOptimizationFlags), - If (!debug, - linkerNoDebugFlags), - If (dynamic, - linkerDynamicFlags), - objectFiles, - linkerKonanFlags, - "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", - "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", + + "$libGcc/crtbegin.o" + + "-L$llvmLib" + + "-L$libGcc" + if (!isMips) + "--hash-style=gnu" // MIPS doesn't support hash-style=gnu + + specificLibs + + listOf("-L$targetSysRoot/../lib", "-L$targetSysRoot/lib", "-L$targetSysRoot/usr/lib") + if (optimize) { + + "-plugin" + +"$llvmLib/LLVMgold.so" + + pluginOptimizationFlags + } + if (optimize) + linkerOptimizationFlags + if (!debug) + linkerNoDebugFlags + if (dynamic) + linkerDynamicFlags + + objectFiles + + linkerKonanFlags + + listOf("-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", + "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed") if (dynamic) - "${libGcc}/crtendS.o" + + "$libGcc/crtendS.o" else - "${libGcc}/crtend.o", - "${targetSysRoot}/usr/lib64/crtn.o" - ) + + "$libGcc/crtend.o" + + "$targetSysRoot/usr/lib64/crtn.o" + } } } @@ -203,18 +234,14 @@ internal open class MingwPlatform(distribution: Distribution) override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib } - override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): List { - return listConstructor( - linker, - "-o", executable, - objectFiles, - If (optimize, - linkerOptimizationFlags), - If (!debug, - linkerNoDebugFlags), - If (dynamic, - linkerDynamicFlags) - ) + override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { + return Command(linker).apply { + + listOf("-o", executable) + + objectFiles + if (optimize) + linkerOptimizationFlags + if (!debug) + linkerNoDebugFlags + if (dynamic) + linkerDynamicFlags + } } override fun linkCommandSuffix() = linkerKonanFlags @@ -230,13 +257,30 @@ internal open class WasmPlatform(distribution: Distribution) override fun filterStaticLibraries(binaries: List) = emptyList() - override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): List { - // No link stage for WASM yet, just give '.wasm' as output. - val src = File(objectFiles.single()) - val dst = File(executable) - src.recursiveCopyTo(dst) + override fun linkCommand(objectFiles: List, executable: ExecutableFile, optimize: Boolean, debug: Boolean, dynamic: Boolean): Command { + return object: Command("") { + override fun execute() { + val src = File(objectFiles.single()) + val dst = File(executable) + src.recursiveCopyTo(dst) + javaScriptLink(libs.filter{it.isJavaScript}, executable) + } - return listOf() + private fun javaScriptLink(jsFiles: List, executable: String): String { + val linkedJavaScript = File("$executable.js") + + val jsLibsExceptLauncher = jsFiles.filter { it != "launcher.js" }.map { it.removeSuffix(".js") } + + val linkerStub = "var konan = { libraries: [] };\n" + + linkedJavaScript.writeBytes(linkerStub.toByteArray()); + + jsFiles.forEach { + linkedJavaScript.appendBytes(File(it).readBytes()) + } + return linkedJavaScript.name + } + } } } @@ -373,52 +417,24 @@ internal class LinkStage(val context: Context) { executable = dylibPath.absolutePath } - // Hack that removes using OS command line environment for copying files. - val toolExecution = platform.linkCommand(objectFiles, executable, optimize, debug, dynamic) - if (toolExecution.isEmpty() && platform is WasmPlatform) { - JavaScriptLinker(includedBinaries.filter{it.isJavaScript}, executable) - return executable - } - - assert(toolExecution.isNotEmpty()) - val linkCommand = toolExecution + - platform.targetLibffi + - asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) + - entryPointSelector + - frameworkLinkerArgs + - platform.linkCommandSuffix() + - platform.linkStaticLibraries(includedBinaries) + - libraryProvidedLinkerFlags - try { - runTool(*linkCommand.toTypedArray()) + platform.linkCommand(objectFiles, executable, optimize, debug, dynamic).apply { + + platform.targetLibffi + + asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) + + entryPointSelector + + frameworkLinkerArgs + + platform.linkCommandSuffix() + + platform.linkStaticLibraries(includedBinaries) + + libraryProvidedLinkerFlags + externalLibraries(includedBinaries) + }.execute() } catch (e: KonanExternalToolFailure) { context.reportCompilationError("linker invocation reported errors") return null } - if (platform is MacOSBasedPlatform && context.shouldContainDebugInfo()) { - if (context.phase?.verbose ?: false) - runTool(*platform.dsymutilDryRunVerboseCommand(executable).toTypedArray()) - runTool(*platform.dsymutilCommand(executable).toTypedArray()) - } return executable } - private fun JavaScriptLinker(jsFiles: List, executable: String): String { - val linkedJavaScript = File("$executable.js") - - val jsLibsExceptLauncher = jsFiles.filter { it != "launcher.js" }.map { it.removeSuffix(".js") } - - val linkerStub = "var konan = { libraries: [] };\n" - - linkedJavaScript.writeBytes(linkerStub.toByteArray()); - - jsFiles.forEach { - linkedJavaScript.appendBytes(File(it).readBytes()) - } - return linkedJavaScript.name - } - private fun executeCommand(vararg command: String): Int { context.log{""}