---
title: Rust ko迁移
description: "Rust编写的ko文件在挂载时，发生如下报错。"
url: https://www.hikunpeng.com/document/detail/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0101.html
sourcePath: /source/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0101.html
indexId: d1445b22bd69364ee019c7f5b380dbd0949d3b7d0cd59c0a49d7cb9061f3a0d965
---
# 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();
}
```
