Skip to content

链接器与符号表

源:Kotlin/Native Linking

链接是 Kotlin/Native 编译的最后阶段。理解链接过程和符号管理能够解决大部分构建问题。

链接过程

编译流程

Kotlin 源码

  K2 Frontend

  Kotlin IR

  LLVM IR

  LLVM 优化

  机器码 (.o)

  链接器 ──→ 可执行文件/库

  依赖库

链接器类型

Kotlin/Native 支持多种链接器:

链接器平台特点
ldLinux/macOS传统链接器
lld全平台LLVM 链接器,速度快
moldLinux现代链接器,极速
link.exeWindowsMSVC 链接器

符号表

符号类型

kotlin
// Kotlin 代码
package com.example

fun publicFunction() { }

internal fun internalFunction() { }

private fun privateFunction() { }

class MyClass {
    fun method() { }
}

生成的符号(简化):

符号表:
- _publicFunction          [GLOBAL]
- _internalFunction        [LOCAL]
- _MyClass_method          [GLOBAL]
- _privateFunction         [LOCAL]

查看符号

bash
# 编译
kotlinc-native main.kt -o app

# 查看符号
nm app.kexe

# 输出示例
0000000000401000 T _ZN3com7example14publicFunctionEv
0000000000401100 t _ZN3com7example16internalFunctionEv
                 U _ZN6kotlin2io7println
bash
nm -g app.kexe  # 只显示全局符号

# 输出
0000000100000000 T _kfun:com.example#publicFunction(){}
                 U _kfun:kotlin.io#println(kotlin.String){}
bash
dumpbin /SYMBOLS app.exe

# 输出
000 00000001 ABS    notype       Static       | @comp.id
001 00000000 SECT1  notype       Static       | .text
002 00000000 SECT1  notype ()    External     | kfun:main

符号修饰 (Name Mangling)

Kotlin/Native 使用名称修饰区分重载函数:

kotlin
// Kotlin
fun add(a: Int, b: Int): Int
fun add(a: Double, b: Double): Double
# 符号表
kfun:add(kotlin.Int,kotlin.Int)kotlin.Int
kfun:add(kotlin.Double,kotlin.Double)kotlin.Double

链接选项

基础链接

bash
# 链接系统库
kotlinc-native main.kt -o app \
    -linker-option -lm       # 数学库
    -linker-option -lpthread  # 线程库

# 链接自定义库
kotlinc-native main.kt -o app \
    -linker-option -L/usr/local/lib \
    -linker-option -lmylib

静态vs动态链接

bash
# 动态链接
kotlinc-native -produce dynamic mylib.kt -o libmylib

# 使用
kotlinc-native app.kt -o app \
    -linker-option -L. \
    -linker-option -lmylib

# 运行时需要 libmylib.so
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./app.kexe
bash
# 静态库
kotlinc-native -produce static mylib.kt -o libmylib

# 使用
kotlinc-native app.kt -o app \
    -linker-option -L. \
    -linker-option -lmylib \
    -linker-option -static

# 独立可执行文件
./app.kexe  # 无需额外库

符号可见性

kotlin
// 控制符号导出
@CName("my_exported_function")
fun exportedFunction() { }

// 内部符号(不导出)
internal fun internalFunction() { }
bash
# Gradle 配置
kotlin {
    linuxX64 {
        binaries.sharedLib {
            // 导出符号配置
            export(project(":shared"))
            
            // 链接器可见性
            freeCompilerArgs += "-Xexport-kdoc"
        }
    }
}

链接错误排查

undefined reference

bash
# 错误信息
undefined reference to `my_function'

# 原因1:缺少库
kotlinc-native main.kt -o app \
    -linker-option -L/path/to/lib \
    -linker-option -lmissing_lib

# 原因2:符号名称不匹配
# 检查实际符号名
nm libmylib.so | grep my_function

# 原因3:链接顺序
# 依赖库应放在后面
kotlinc-native main.kt -o app \
    -linker-option -lmylib \  # 使用 myfunction
    -linker-option -lbase     # 被 mylib 依赖

duplicate symbol

bash
# 错误
duplicate symbol '_kfun:main' in:
    obj1.o
    obj2.o

# 原因:多个文件定义同名函数
# 解决:使用不同的包名或internal

// file1.kt
package com.example.module1
fun main() { }

// file2.kt
package com.example.module2
fun main() { }

符号版本冲突

bash
# GLIBC 版本错误
./app: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found

# 解决:在旧系统上编译,或静态链接
kotlinc-native main.kt -o app \
    -linker-option -static-libgcc \
    -linker-option -static-libstdc++

高级技巧

链接脚本

ld
/* linker.ld */
SECTIONS
{
    .text : { *(.text*) }
    .data : { *(.data*) }
    .bss  : { *(.bss*) }
}
bash
# 使用自定义链接脚本
kotlinc-native main.kt -o app \
    -linker-option -T \
    -linker-option linker.ld

减小二进制大小

bash
# 1. 启用优化
kotlinc-native -opt -Xlto main.kt

# 2. 移除调试符号
strip app.kexe
# 减少 30-40%

# 3. 压缩(UPX)
upx --best app.kexe
# 额外减少 50-70%

符号隐藏

kotlin
// 在 Gradle 中配置
kotlin {
    linuxX64 {
        binaries.sharedLib {
            // 默认隐藏所有符号
            freeCompilerArgs += "-Xvisibility=hidden"
            
            // 仅导出标记的符号
            exportDeclarations {
                export("com.example.publicAPI")
            }
        }
    }
}

性能影响

链接器对比

链接器链接时间 (大项目)内存占用支持平台
ld基准 (100s)Linux/macOS
lld30-40s (3x)全平台
mold10-15s (10x)Linux

启用 lld

kotlin
// build.gradle.kts
kotlin {
    targets.withType<KotlinNativeTarget> {
        binaries.all {
            // 使用 lld
            freeCompilerArgs += "-linker-option"
            freeCompilerArgs += "--use-lld"
        }
    }
}

理解链接过程能够避免 90% 的构建问题。遇到链接错误时,首先检查符号表,然后验证库路径和链接顺序。