Rust ko迁移
现象描述
Rust编写的ko文件在挂载时,发生如下报错。
报错信息:
# insmod build/test.ko insmod: ERROR: could not insert module build/test.ko: Invalid module format # dmesg [2250768.195543] module test: unsupported RELA relocation: 311

在上述报错信息对应的代码文件中,是由于Rust试图直接访问C文件中的全局变量而产生的。
问题原因
在使用Rust自带编译器Rustc进行编译运行程序的过程中,其最后的链接工作默认是交给后端的llvm链接器进行链接的。但是如果是使用Rustc编译ko文件,其链接工作是要给内核来进行。
但是截止至Linux内核版本4.19.5,当前ARM Linux内核不支持311等多种重定位类型。
以下示例展示在Linux内核版本4.19.5内核源码中elf.h文件的代码,列出了当前内核版本支持的重定位类型。
/* Miscellaneous. */ #define R_ARM_NONE 0 #define R_AARCH64_NONE 256 /* Data. */ #define R_AARCH64_ABS64 257 #define R_AARCH64_ABS32 258 #define R_AARCH64_ABS16 259 #define R_AARCH64_PREL64 260 #define R_AARCH64_PREL32 261 #define R_AARCH64_PREL16 262 /* Instructions. */ #define R_AARCH64_MOVW_UABS_G0 263 #define R_AARCH64_MOVW_UABS_G0_NC 264 #define R_AARCH64_MOVW_UABS_G1 265 #define R_AARCH64_MOVW_UABS_G1_NC 266 #define R_AARCH64_MOVW_UABS_G2 267 #define R_AARCH64_MOVW_UABS_G2_NC 268 #define R_AARCH64_MOVW_UABS_G3 269 #define R_AARCH64_MOVW_SABS_G0 270 #define R_AARCH64_MOVW_SABS_G1 271 #define R_AARCH64_MOVW_SABS_G2 272 #define R_AARCH64_LD_PREL_LO19 273 #define R_AARCH64_ADR_PREL_LO21 274 #define R_AARCH64_ADR_PREL_PG_HI21 275 #define R_AARCH64_ADR_PREL_PG_HI21_NC 276 #define R_AARCH64_ADD_ABS_LO12_NC 277 #define R_AARCH64_LDST8_ABS_LO12_NC 278 #define R_AARCH64_TSTBR14 279 #define R_AARCH64_CONDBR19 280 #define R_AARCH64_JUMP26 282 #define R_AARCH64_CALL26 283 #define R_AARCH64_LDST16_ABS_LO12_NC 284 #define R_AARCH64_LDST32_ABS_LO12_NC 285 #define R_AARCH64_LDST64_ABS_LO12_NC 286 #define R_AARCH64_LDST128_ABS_LO12_NC 299 #define R_AARCH64_MOVW_PREL_G0 287 #define R_AARCH64_MOVW_PREL_G0_NC 288 #define R_AARCH64_MOVW_PREL_G1 289 #define R_AARCH64_MOVW_PREL_G1_NC 290 #define R_AARCH64_MOVW_PREL_G2 291 #define R_AARCH64_MOVW_PREL_G2_NC 292 #define R_AARCH64_MOVW_PREL_G3 293 #define R_AARCH64_RELATIVE 1027
以下列表展示截止至Linux内核版本4.19.5,内核源码中所不支持的40种重定位类型。
ELF64Code |
ELF32Code |
Operation |
---|---|---|
279 |
18 |
R_<CLS>_TSTBR14 |
280 |
19 |
R_<CLS>_CONDBR19 |
282 |
20 |
R_<CLS>_JUMP26 |
283 |
21 |
R_<CLS>_CALL26 |
300 |
- |
R_<CLS>_MOVW_GOTOFF_G0 |
301 |
- |
R_<CLS>_MOVW_GOTOFF_G0_NC |
302 |
- |
R_<CLS>_MOVW_GOTOFF_G1 |
303 |
- |
R_<CLS>_MOVW_GOTOFF_G1_NC |
304 |
- |
R_<CLS>_MOVW_GOTOFF_G2 |
305 |
- |
R_<CLS>_MOVW_GOTOFF_G2_NC |
306 |
- |
R_<CLS>_MOVW_GOTOFF_G3 |
307 |
- |
R_<CLS>_GOTREL64 |
308 |
- |
R_<CLS>_GOTREL32 S+A-GOT Set the data to a 32-bit offset relative to GOT, treated as signed; check that -231 <= X < 231 |
309 |
25 |
R_<CLS>_GOT_LD_PREL19 G(GDAT(S+A))- P Set a load-literal immediate field to bits [20:2] of X; check -220 <= X < 220 |
310 |
- |
R_<CLS>_LD64_GOTOFF_LO15 G(GDAT(S+A))- GOT Set a LD/ST immediate field to bits [14:3] of X; check that 0 <= X < 215, X&7 = 0 |
311 |
26 |
R_<CLS>_ADR_GOT_PAGE Page(G(GDAT(S+A)))-Page(P) Set the immediate value of an ADRP to bits [32:12] of X; check that -232 <= X < 232 |
312 |
- |
R_<CLS>_LD64_GOT_LO12_NC G(GDAT(S+A)) Set the LD/ST immediate field to bits [11:3] of X. No overflow check; check that X&7 = 0 |
313 |
- |
R_<CLS>_LD64_GOTPAGE_LO15 G(GDAT(S+A))-Page(GOT) Set the LD/ST immediate field to bits [14:3] of X; check that 0 <= X < 215, X&7 = 0 |
在鲲鹏平台下,对于可执行文件,其链接工作是交给链接器执行,对于LLVM编译器,其对应的链接器支持上表所示的重定位类型,因此不会有链接重定位报错问题产生。但由于ko文件的链接工作是交给系统内核去执行,且当前ARM Linux内核不支持311等多种重定位类型,因此如果在Rust代码中涉及Rust直接调用C/C++文件中的全局变量,在ko文件挂载的过程中会报311重定位错误。
因此,在x86平台下,Rust代码可以直接调用c代码的全局变量,而在鲲鹏平台下,需通过Rust FFI的形式,通过函数将C文件中全局变量的指针传给Rust,完成调用,来规避311重定位错误。
处理步骤
Rust提供了FFI(Foreign Function Interface)机制来调用外部函数,因此可以使用这种机制,通过函数的方式来完成外部全局变量的调用。
如要调用c文件中的全局变量,需在c代码文件中,设置一个专门的函数返回对应的全局变量的地址,然后在Rust代码声明函数接口,并调用,获取该函数获取该全局变量的地址。
以下代码示例展示了如何使用Rust调用c文件中的全局变量global_a。
- x86中原代码如下:
//Rust代码中变量pama1可直接调用c文件中全局变量global_a // c 文件中 int global_a = 1; // Rust文件中 extern "C" { static mut global_a:u64; } unsafe { let mut pama1 = global_a; }
- 鲲鹏中需按如下方式实现:
// c 文件中 int global_a = 1; int* GetInt(void) { return &global_a; } // Rust文件中 extern "C" { pub fn GetInt()->*mut ::libc::c_int; } unsafe { let mut pama1 = GetInt(); }