Rust编译过程讲解与环境准备

1.了解编译过程

目前主流编译平台有,GNU、MSVC、LLVM。因为rustc调用了llvm,因此我们以LLVM为例,我们从C语言的编译过程聊,再对比Rust,看它们的编译过程有何差异。

clang下载链接: releases.llvm.org/download.ht...

shell 复制代码
# 保存编译过程中的临时文件
$ clang -save-temps hello.c 
# 打印编译阶段
$ clang -ccc-print-phases hello.c
            +- 0: input, "hello.c", c -----------> hello.c >
         +- 1: preprocessor, {0}, cpp-output ----> hello.i
      +- 2: compiler, {1}, ir -------------------> hello.bc
   +- 3: backend, {2}, assembler ----------------> hello.s
+- 4: assembler, {3}, object --------------------> hello.o
5: linker, {4}, image ---------------------------> a.exe

第一步:预处理

输入是hello.c,输出是hello.i

c 复制代码
#include <stdio.h>
int main() {
    printf("Hello World!\n");
    return 0;
}
shell 复制代码
# 等价的gcc指令:gcc -E hello.c -o hello.i
$clang -E -c hello.c -o hello.i
# 查看.i文件内容
$cat hello.i
  __attribute__((__format__ (scanf, 2, 0))) __attribute__ ((__nonnull__ (2)))
...
# 1650 "D:/usr/msys2/clang64/include/stdio.h" 2 3
# 2 "hello.c" 2
int main() {
    printf("Hello World!\n");
    return 0;
}

将.i 文件导出为LLVM IR后以备下一步使用

shell 复制代码
clang -emit-llvm hello.i -c -o hello.bc # 导出二进制的LLVM IR
clang -emit-llvm hello.c -S -o hello.ll # 导出文本类型的LLVM IR

第二部:编译

将预处理完的文件进行一些列的词法分析、语法分析、语义分析和优化后生成的汇编指令代码。 这一步我们就可以使用LLVM的llc命令对上一步的IR文件编译了。

shell 复制代码
# 等价的gcc指令:gcc -S hello.i -o hello.s
$llc hello.ll -o hello.s
shell 复制代码
$ cat hello.s
        .text
        .def    @feat.00;
        .scl    3;
        .type   0;
        .endef
        .globl  @feat.00
.set @feat.00, 0
        .file   "hello.c"
        .def    main;
        .scl    2;
        .type   32;
        .endef
        .globl  main                            # -- Begin function main
        .p2align        4, 0x90
main:                                   # @main
.seh_proc main
# %bb.0:
        pushq   %rbp
        .seh_pushreg %rbp
        subq    $48, %rsp
        .seh_stackalloc 48
        leaq    48(%rsp), %rbp
        .seh_setframe %rbp, 48
        .seh_endprologue
        callq   __main
        movl    $0, -4(%rbp)
        leaq    .L.str(%rip), %rcx
        callq   printf
        xorl    %eax, %eax
        addq    $48, %rsp
        popq    %rbp
        retq
        .seh_endproc
                                        # -- End function
        .section        .rdata,"dr"
.L.str:                                 # @.str
        .asciz  "Hello\302\240World!\n"

第三步:汇编

把汇编代码转变成机器可以执行的指令,过程相对编译阶段简单,没有复杂的语法,也不需要优化,只需要对照汇编指令和机器指令对照表一一翻译即可。

shell 复制代码
#等价的gcc指令:gcc -c add.s -o add.o
clang -fmodules -c hello.s -o hello.o

第四步:链接

目标文件和依赖的库 打包成一个可执行文件

shell 复制代码
clang hello.o -o hello

链接分为静态链接和动态链接。。

总结

到现在我们就可以回答一个问题:编译器究竟做了什么呢?

首先就是将源码 转换为目标平台可以直接识别的指令文件 。分为两类:可执行文件。 在编译最后产生的image,不同操作系统有不同的格式(这里的格式指的是文件的布局结构),在Windows通常是PE,Linux上则是ELF。

ELF格式

现在我们得到了可执行文件,我们在思考可执行文件究竟是什么? 答案就是可执行文件内包含了初始状态的进程数据。

通常可执行文件、目标文件、静态链接库(Linux的.a,Windows的.obj)和动态链接库(Linux的.so,Windows的DLL)都是ELF格式的文件

ELF文件中主要包含程序指令程序数据

ELF的结构:

  • File Header 主要包含了文件是否为可执行文件、目标硬件、目标操作系统、段表等。段表描述了各个段在文件中的偏移等信息。
  • .text section 代码段
  • .data section 数据段
  • .bss section 未初始化的全局变量和局部静态变量,在文件中不占空间。
  • ...

od -x ./hello # 以16进制查看文件 xxd -b ./hello # 以2进制查看文件 hexdump -C ./hello # 以16进制查看文件 file ./add # 查看文件的头信息 ldd ./add # 查看可执行文件依赖的动态库 objdump -h ./add # 打印ELF文件的各个段 size ./add # 查看ELF各个段的长度 readelf -h ./add # 查看ELF文件的信息 clang -ccc-print-phases hello.c # 查看编译过程

Rust中的编译过程

通过前面的介绍,我们知道LLVM有一个好处,就是将前端和后端通过IR中间语言隔离开了。

这样一来,Rust只需要实现一个前端就可以了。Rust实现的编译器就是rustc.exe,它就包含了rust前端编译器,LLVM和调用连接器。连接器后续极有可能也会使用llvm提供的连接器,目前还是使用mvsc或者GNU的连接器,这也是为什么安装Rust时,需要单独安装vs环境或者gcc环境的原因。

把这个过程说清楚之后,下一节我们来实践安装Rust。

2.安装Rust

如果你只是想体验一下Rust或者快速验证想法可以访问play.rust-lang.org

正式的学习和开发还是需要在你的本地安装Rust,这就用到了Rustup。

Rustup是Rust社区提供的工具链管理工具,使用Rustup来安装和管理工具链,并且可以随时切换工具链的版本。访问地址: rustup.rs/

本节课用到的软件可以直接通过官网下载或者加群获取:添加微信code2c,回复"加群"

在windows11安装Rust

这里以windows11为例,其它版本也是类似。在windows平台有两个版本可供选择:

x86_64-pc-windows-gnu x86_64-pc-windows-msvc

GNU 和 MSVC 都是编译器工具链,它们提供一套工具和库,这些工具和库被用来构建、链接和调试应用程序。Rust使用了它们提供的连接器 ,其实还有第三种选择就是LLVM,我认为Rust最终会在各个平台使用LLVM的连接器来取代目前的GNU和MSVC的地位,因为rust编译器在汇编阶段使用的就是LLVM,为了统一起来极有可能会采用LLVM的连接器,但目前这项工作还没有完成。为了获得更好的兼容性和运行效率,我们在windows平台上选择MSVC。

www.rust-lang.org/zh-CN/learn...

下面的命令窗口提示,你的电脑缺少vc++环境(连接器和windows api库),也就是需要安装MSVC。

这里我们选择第一项,他会下载vs的安装包。。。等待它下载完成。

下载完成会弹出安装弹框,点击"继续",接下来会下载安装程序并安装vs。

msvc与windows sdk必须勾选,然后点击"安装"

出现下面界面就说明已经安装成功了,就可以关掉了。

关掉vs,回到cmd继续安装rust,选择第一项默认安装即可。

到这一步下载工具链并设置好PATH环境变量。

当看到上面这个提示就说明已经安装完成了,最后测试rust是否安装成功

shell 复制代码
# 查看rustup版本
rustup -V
# 查看工具链
rustup show
# 查看cargo版本
cargo -V
# 查看编译器版本
rustc -V

在Linux安装Rust

Hello world

任何一门编程语言的入门都少不了Hello world,我们快速写一个helloworld程序,准备玩起来: 创建一个文件hello.rs

rust 复制代码
// hello.rs
fn main() {
    println!("Hello, world!");
}

编译并执行:

shell 复制代码
$ rustc hello.rs
$ .\hello.exe # on Windows

接下来我们需要选择一个趁手的编辑器,目前有vim、vscode和去年jetbrains推出的RustRover,可以根据你的喜好选择。我强烈建议你试用一下RustRover,虽然它还在测试阶段。 www.jetbrains.com/rust/

经过上面的步骤,我们已经能愉快开发rust项目。 但是开发项目通常会有一套规范或者说最佳实践,来管理项目依赖和约定目录结构,不至于像早期c/c++每个项目都有一套自己规范。现代语言通常都有的工具,在js中有npm,java中有maven,rust同样提供了类似的工具来管理项目就是cargo。有了cargo我们就可以创建rust package,构建以及发布package到官方仓库。

下面简单演示如何使用cargo新建一个项目:

shell 复制代码
$ cargo new hello_world
$ cd hello_world
$ tree .
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

cargo的基本知识在下一节中介绍。

参考和拓展

相关推荐
想用offer打牌10 分钟前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅42 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX2 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法2 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端