中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

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提供了FFIForeign 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();
    }