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(); }