嵌入式技术论坛
直播中

abdkjshd

8年用户 1168经验值
擅长:可编程逻辑
私信 关注
[经验]

如何利用C语言去调用rust静态库呢

引言
工作中的嵌入式项目,基本都是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

更多回帖

发帖
×
20
完善资料,
赚取积分