引言
工作中的嵌入式项目,基本都是C语言。
一直想在项目中引入一个略高级的语言,来填补C语言的一些不足。
之前有用过MicroPython和javascript,但除了性能和体积外,都有些要把主要工作转到新语言的感觉,要做不少的对接工作。
也用过Lua,感觉也差不多。
评估
学习评估Rust语言时,感觉性能和体积应该都不会有太大的问题。加上语言本身主打的安全性,再结合一些库,用来做一些C语言不擅长的动态操作感觉比较合适。
但如果把主要工作切过来,感觉Rust目前又太荒芜了,而且上面的问题也同样存在。
尝试
了解到Rust可以编译成静态库,于是动了只用Rust实现其中一小部分功能的想法。
随手一搜,找到这篇文章: c语言调用rust库函数
按步骤做完,倒是挺顺利,增强了信心。
编译arm版静态库
上面测试都是在x86上面进行的,嵌入式基本是使用arm和riscv等芯片。
考虑到上手门槛,我这里选择了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.
对于没有移植标准库的目标环境,需要添加 #![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 macroprintlnin 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
#include
#include
extern int foo(int a, int b); // 声明一个函数
int main(void)
{
int tmp;
printf("hello rt-threadn");
tmp = foo(1, 2);
printf("call rust fn: foo(1, 2) == %dn", tmp);
return 0;
}
在链接时发生报了错误
$ scons --verbose
... -Lapplicationslibs -lfoo -lc -lm
/armv7-ar/thumblibgcc.a(_arm_addsubdf3.o): In function `__aeabi_ul2d':
(.text+0x304): multiple definition of `__aeabi_ul2d'
applicationslibslibfoo.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
... -Lapplicationslibs -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 :
60010054: fa01f69b blx 6008dac8
60010058: e3a01002 mov r1, #2
6001005c: e3a00001 mov r0, #1
60010060: eb01f2ea bl 6008cc10
6008cc10 :
6008cc10: e0810000 add r0, r1, r0
6008cc14: e12fff1e bx lr
可以看到,纯运算类的体积与性能,与C版本完全一致, 其它高级特性可能会对堆的使用较多,但只要遵守rust的规范,写穿溢出的可能性应该极小。 纯运算和对栈的使用,应该不会有太大的差异。
尝试用来解决一些工作中的实际问题,只有被真实需求推动才能持续。
rust整个结构还不是特别清晰,特别是库和wrapper相关的
同个C项目,包含多个rust的静态库,静态库中也会有符号冲突
这点应该可以把多个rust程序打包一个库来解决。
能否准确评估出对栈的需求。
原作者:aozima
|