原文链接:https://club.rt-thread.org/ask/article/2439.html
工作中的嵌入式项目,基本都是C语言。
一直想在项目中引入一个略高级的语言,来填补C语言的一些不足。
之前有用过MicroPython)和javascript,但除了性能和体积外,都有些要把主要工作转到新语言的感觉,要做不少的对接工作。
也用过Lua,感觉也差不多。
学习评估Rust)语言时,感觉性能和体积应该都不会有太大的问题。加上语言本身主打的安全性,再结合一些库,用来做一些C语言不擅长的动态操作感觉比较合适。
但如果把主要工作切过来,感觉Rust目前又太荒芜了,而且上面的问题也同样存在。
## 尝试
了解到Rust可以编译成静态库,于是动了只用Rust实现其中一小部分功能的想法。
随手一搜,找到这篇文章: c语言调用rust库函数]
按步骤做完,倒是挺顺利,增强了信心。
### 编译arm版静态库
上面测试都是在x86上面进行的,嵌入式基本是使用arm和riscv等芯片。
考虑到上手门槛,我这里选择了[qemu-vexpress-a9](https://gitee.com/rtthread/rt-thread/tree/master/bsp/qemu-vexpress-a9)这个bsp做为我们的目标平台。
这样不用开发板就可以测试了。
先要安装目标环境,可以参考这个链接:Rust 嵌入式开发 STM32 和 RISC-V)
rustup target list
rustup target add armv7a-none-eabi
rustup target list | grep installed
安装好目标环境后,开始尝试编译arm版的静态库
$ cat src/lib.rs
#[no_mangle]
pub extern "C" fn foo(a: i32, b: i32) {
println!("hello : a + b = {}", a + b);
}
$ cargo build --target=armv7a-none-eabi
warning: unused manifest key: build
Compiling foo v0.1.0 (/home/aozima/work/rust_study/foo)
error[E0463]: can't find crate for `std`
|
= note: the `armv7a-none-eabi` target may not be installed
error: aborting due to previous error
For more information about this error, try `rustc --explain E0463`.
error: could not compile `foo`
To learn more, run the command again with --verbose.
查了一些资料
* https://forge.rust-lang.org/release/platform-support.html
* https://blog.csdn.net/readlnh/article/details/88899586
对于没有移植标准库的目标环境,需要添加 `#![no_std]`
$ cat src/lib.rs
#![no_std]
#[no_mangle]
pub extern "C" fn foo(a: i32, b: i32) {
println!("hello : a + b = {}", a + b);
}
这下错误提示变成了
`cannot find macro `println` in this scope`
考虑到先简单做测试,于是先注释掉打印,改为纯运算。
$ cat src/lib.rs
#![no_std]
#[no_mangle]
pub extern "C" fn foo(a: i32, b: i32) -> i32 {
//println!("hello : a + b = {}", a + b);
return a+b;
}
这下错误提示变成了
`error: `#[panic_handler]` function required, but not found`
参考资料:独立式可执行程序,添加必要的 `panic`
$ cat src/lib.rs
#![no_std]
use core::panic::PanicInfo;
#[no_mangle]
pub extern "C" fn foo(a: i32, b: i32) -> i32 {
//println!("hello : a + b = {}", a + b);
return a+b;
}
#[panic_handler]
fn panic(_info:&PanicInfo) -> !{
loop{}
}
终于成功编译
$ cargo build --target=armv7a-none-eabi
warning: unused manifest key: build
Compiling foo v0.1.0 (/home/aozima/work/rust_study/foo)
Finished dev [unoptimized + debuginfo] target(s) in 0.10s
$ ls target/armv7a-none-eabi/debug/libfoo.a
## 使用静态库
把编译出来的 libfoo.a 复制到 qemu-vexpress-a9的`applications/libs`目录下,
并更新 `applications/SConscript`
-group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)
+LIBS = ["libfoo.a"] # 添加静态库文件
+LIBPATH = [os.path.join(GetCurrentDir(), 'libs')] # 添加静态库搜索路径
+
+group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH, LIBS = LIBS, LIBPATH = LIBPATH)
### C中引用静态库中的函数
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
extern int foo(int a, int b); // 声明一个函数
int main(void)
{
int tmp;
printf("hello rt-thread\n");
tmp = foo(1, 2);
printf("call rust fn: foo(1, 2) == %d\n", tmp);
return 0;
}
在链接时发生报了错误
$ scons --verbose
... -Lapplications\libs -lfoo -lc -lm
/armv7-ar/thumb\libgcc.a(_arm_addsubdf3.o): In function `__aeabi_ul2d':
(.text+0x304): multiple definition of `__aeabi_ul2d'
applications\libs\libfoo.a(compiler_builtins-9b744f6fddf5e719.compiler_builtins.20m0qzjq-cgu.117.rcgu.o):
/cargo/registry/src/github.com-1ecc6299db9ec823/compiler_builtins-0.1.35/src/float/conv.rs:143: first defined here
提示在rust的静态库libfoo.a中也有__aeabi_ul2d的实现,与libgcc.a中冲突。
这点暂时没理解得太清楚,不过release版本编译的库没有引入这个实现
$ cargo build --target=armv7a-none-eabi --release
$ ls target/armv7a-none-eabi/release/libfoo.a
把release版的libfoo.a复制到`applications/libs`目录下,
再次编译链接通过
$ scons --verbose
... -Lapplications\libs -lfoo -lc -lm
arm-none-eabi-objcopy -O binary rtthread.elf rtthread.bin
arm-none-eabi-size rtthread.elf
text data bss dec hex filename
593317 5212 85056 683585 a6e41 rtthread.elf
scons: done building targets.
在qemu中运行成功
\ | /
- RT - Thread Operating System
/ | \ 4.0.3 build Dec 26 2020
2006 - 2020 Copyright by rt-thread team
lwIP-2.0.2 initialized!
hello rt-thread
call rust fn: foo(1, 2) == 3
msh />
## 体积与性能分析
把生成的elf进行反汇编
60010040 <main>:
60010054: fa01f69b blx 6008dac8 <puts>
60010058: e3a01002 mov r1, #2
6001005c: e3a00001 mov r0, #1
60010060: eb01f2ea bl 6008cc10 <foo>
6008cc10 <foo>:
6008cc10: e0810000 add r0, r1, r0
6008cc14: e12fff1e bx lr
可以看到,纯运算类的体积与性能,与C版本完全一致,
其它高级特性可能会对堆的使用较多,但只要遵守rust的规范,写穿溢出的可能性应该极小。
纯运算和对栈的使用,应该不会有太大的差异。
最近编辑记录 aozima (2020-12-27 22:33:39)
离线