您尚未登录。

#1 2020-12-27 22:27:52

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
累计积分: 156
个人网站

有没玩rust的?最近打算把rust程序编译成静态库,然后在C里面调用

原文链接: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)

离线

#2 2020-12-28 17:15:36

达克罗德
会员
注册时间: 2018-04-10
累计积分: 979

Re: 有没玩rust的?最近打算把rust程序编译成静态库,然后在C里面调用

顶一个,这个是很好的尝试。我有一段时间自学了Rush,感觉比Go要好点。

离线

#3 2020-12-29 10:11:11

aozima
会员
所在地: 深圳
注册时间: 2019-05-25
累计积分: 156
个人网站

Re: 有没玩rust的?最近打算把rust程序编译成静态库,然后在C里面调用

Rush? Rust?

Rust有很多有用的特性,但全转到Rust感觉有点自废武功,所以希望可以结合起来,
并解决项目中的实际问题。

目前还在摸索中,欢迎楼上入坑。

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn