Rate This Document
Findability
Accuracy
Completeness
Readability

Porting Rust .ko Files

Symptom

The following error is reported when the .ko file compiled in Rust is mounted:

Error message:

# 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

The preceding error is generated due to the fact that the Rust code attempts to access the global variables in the C file.

Cause

When rustc, the built-in Rust compiler, is used to compile and run programs, the linking operation is handed over to the backend LLVM linker by default. However, if the .ko file is compiled using rustc, the linking operation is performed by the kernel.

However, as of Linux kernel version 4.19.5, the current ARM Linux kernel does not support multiple relocation types, such as 311.

The following example shows the code of the elf.h file in the Linux kernel source code (version 4.19.5), listing the relocation types supported by the current kernel version.

/* 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

The following table lists the 40 relocation types that are not supported by the kernel source code (as of Linux kernel version 4.19.5).

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

On the Kunpeng platform, the linking operation on executable files is executed by the linker. For the LLVM compiler, the linker supports the relocation types listed in the preceding table. Therefore, no link relocation error is reported. However, the linking operation on the .ko file is executed by the OS kernel, and the current ARM Linux kernel does not support multiple relocation types such as 311. Therefore, if Rust code directly calls the global variables in the C or C++ file, a 311 relocation error is reported during the mounting of the .ko file.

Therefore, on the x86 platform, the Rust code can directly call the global variables of the C code. On the Kunpeng platform, the pointers of the global variables in the C file need to be transferred to Rust using the FFI (foreign function interface) to avoid the 311 relocation error.

Procedure

Rust provides the FFI to call external functions. This mechanism can be used to call external global variables through functions.

To call a global variable in a C file, you need to set a dedicated function in the C file to return the address of the global variable, declare the function in the Rust code, and call the function to obtain the address of the global variable.

The following code example shows how to use Rust to call the global variable global_a in the C file.

  • Code implementation on the x86 platform:
    //The global variable global_a in the C file can be directly called as the value of the papa1 variable in the Rust code.
    // In the C file
    int global_a = 1; 
     
    // In the Rust file
    extern "C" {
        static mut global_a:u64;
    }
    unsafe {
     let mut pama1 = global_a;
    }
  • Code implementation on the Kunpeng platform:
    // In the C file
    int global_a = 1; 
    int* GetInt(void) {
    return &global_a;
    }
     
    // In the Rust file
    extern "C" {
        pub fn GetInt()->*mut ::libc::c_int;
    }
     
    unsafe {
     let mut pama1 = GetInt();
    }