- 适用于 JDK 23 的 GraalVM(最新版)
- 适用于 JDK 24 的 GraalVM(抢先体验版)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发版本
编译到 LLVM 位码
GraalVM 可以执行 C/C++、Rust 和其他可以编译到 LLVM 位码的语言。第一步,您需要使用一些 LLVM 编译器前端将程序编译到 LLVM 位码,例如,使用 clang
编译 C 和 C++,使用 rust
编译 Rust 编程语言等。
文件格式 #
虽然 GraalVM LLVM 运行时可以执行 纯位码文件,但首选格式是包含嵌入式位码的原生可执行文件。可执行文件格式在 Linux 和 macOS 上有所不同。Linux 默认使用 ELF 文件。位码存储在名为 .llvmbc
的节中。macOS 平台使用 Mach-O 文件。位码位于 __LLVM
段的 __bundle
节中。
与纯位码文件相比,使用包含嵌入式位码的原生可执行文件有两个优势。首先,原生项目的构建系统(例如 Makefile
)期望结果为可执行文件。嵌入位码而不是更改输出格式可以提高与现有项目的兼容性。其次,可执行文件允许指定库依赖关系,而 LLVM 位码则无法做到这一点。GraalVM LLVM 运行时利用此信息来查找和加载依赖关系。
用于编译 C/C++ 的 LLVM 工具链 #
为了简化将 C/C++ 编译到包含嵌入式位码的可执行文件,LLVM 运行时附带了一个预构建的 LLVM 工具链。该工具链包含编译器(例如,用于 C 的 clang
或用于 C++ 的 clang++
),以及构建原生项目所需的工具(例如,链接器(ld
)或用于创建静态库的归档器(ar
))。
- 使用
lli
的--print-toolchain-path
参数获取工具链的位置。./path/to/bin/lli --print-toolchain-path
- 设置
LLVM_TOOLCHAIN
环境变量。export LLVM_TOOLCHAIN=$(./path/to/bin/lli --print-toolchain-path)
- 然后查看工具链路径的内容,以获取可用工具列表。
ls $LLVM_TOOLCHAIN
就像您进行原生编译一样使用这些工具。例如,将此 C 代码保存到名为 hello.c
的文件中。
#include <stdio.h>
int main() {
printf("Hello from GraalVM!\n");
return 0;
}
然后,您可以使用以下命令将 hello.c
编译到包含嵌入式 LLVM 位码的可执行文件。
$LLVM_TOOLCHAIN/clang hello.c -o hello
生成的 hello
可执行文件可以使用 lli
在 GraalVM 上执行。
$JAVA_HOME/bin/lli hello
外部库依赖关系 #
如果位码文件依赖于外部库,GraalVM 会自动从二进制文件头中获取依赖关系。例如。
#include <unistd.h>
#include <ncurses.h>
int main() {
initscr();
printw("Hello, Curses!");
refresh();
sleep(1);
endwin();
return 0;
}
然后,可以编译和运行此 hello-curses.c 文件。
$LLVM_TOOLCHAIN/clang hello-curses.c -lncurses -o hello-curses
lli hello-curses
运行 C++ #
要运行 C++ 代码,GraalVM LLVM 运行时需要 LLVM 项目中的 libc++
标准库。GraalVM 附带的 LLVM 工具链会自动链接到 libc++
。例如,将此代码保存到名为 hello-c++.cpp 的文件中。
#include <iostream>
int main() {
std::cout << "Hello, C++ World!" << std::endl;
}
使用 GraalVM 附带的 clang++
编译它并执行。
$LLVM_TOOLCHAIN/clang++ hello-c++.cpp -o hello-c++
lli hello-c++
Hello, C++ World!
运行 Rust #
GraalVM 附带的 LLVM 工具链不包含 Rust 编译器。要安装 Rust,请在命令提示符中运行以下命令,然后按照屏幕上的说明进行操作。
curl https://sh.rustup.rs -sSf | sh
将此示例 Rust 代码保存到名为 hello-rust.rs 的文件中。
fn main() {
println!("Hello Rust!");
}
然后可以使用 --emit=llvm-bc
标志将其编译到位码。
rustc --emit=llvm-bc hello-rust.rs
要运行 Rust 程序,我们必须告诉 GraalVM 在哪里可以找到 Rust 标准库。
lli --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
Hello Rust!
由于 Rust 编译器没有使用 GraalVM 附带的 LLVM 工具链,因此根据本地 Rust 安装,可能会出现以下错误之一。
Mismatching target triple (expected x86_64-unknown-linux-gnu, got x86_64-pc-linux-gnu)
Mismatching target triple (expected x86_64-apple-macosx10.11.0, got x86_64-apple-darwin)
这表明 Rust 编译器使用的目标三元组与 GraalVM 附带的 LLVM 工具链不同。在本例中,差异仅仅是不同 Linux 发行版或 MacOS 版本的命名约定不同,并没有实质上的差异。在这种情况下,可以安全地忽略错误。
lli --experimental-options --llvm.verifyBitcode=false --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
此选项应仅在手动验证目标三元组确实兼容后使用,即体系结构、操作系统和 C 库都匹配。例如,x86_64-unknown-linux-musl
和 x86_64-unknown-linux-gnu
确实不同,位码是针对不同的 C 库编译的。--llvm.verifyBitcode=false
选项禁用所有检查,GraalVM 然后将尝试运行位码,无论如何,这可能会以不可预知的方式随机失败。