交叉编译arm linux内核模块

第一种方法

需要编译整个内核

参考文章:https://blukat.me/2017/12/cross-compile-arm-kernel-module/、

步骤如下:

  1. 下载arm交叉编译器:

    linaro社区,老版本 :版本范围 4.9 ~ 7.5

    arm官网,新版本 : 版本范围 8.2 ~10.3

  2. 下载对应版本的linux内核:https://mirrors.edge.kernel.org/pub/linux/kernel/

  3. 获取.config文件(optee场景下,编译一次后,在linux源码目录下有该文件)

  4. 编译linux内核

    1
    make ARCH=arm CROSS_COMPILE=<TOOLCHAIN_DIR>/bin/arm-linux-gnueabihf-
  5. 编译内核模块

第二种方法

使用原有的.config文件,无需编译整个内核

参考文章:https://huhaipeng.top/2019/02/01/linux内核模块的交叉编译和加载/

optee 3.15.0 qemu版本为例,它是arm架构的,ree侧运行linux kernel。我希望在本地x86 linux上编译一个ko,且该ko能在该qemu arm linux中insmod并运行。(optee搭建可以参考我的另一篇文章 - 基于qemu的optee模拟环境搭建

这涉及到交叉编译,步骤比较复杂,且暂未明白每一步骤的具体含义,因此详细记录一下我的操作过程。

环境准备

我的目标是编译一个arm版的linux内核模块,那么肯定离不开两个东西:

(1)linux内核源码

(2)交叉编译器

下载这两个文件(注意一定要跟目标系统的版本匹配哦):

1
2
3
4
5
cd /home/bling/Downloads/
# 下载optee对应版本的linux内核源码
git clone -b optee-3.15.0 --depth=1 https://github.com/linaro-swg/linux.git
# 下载交叉编译工具链
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz

重点来了,需要编译optee时linux目录下生成的.config文件,将其拷贝到当前linux目录下。然后再继续后面的步骤。

在源码目录下依次执行如下3条命令:

1
2
3
4
5
6
7
# 根据默认.config进行设置,在menuconfig中save即可
make ARCH=arm CROSS_COMPILE=/home/bling/Downloads/gcc-arm-aarch32/bin/arm-none-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=/home/bling/Downloads/gcc-arm-aarch32/bin/arm-none-linux-gnueabihf- prepare
make ARCH=arm CROSS_COMPILE=/home/bling/Downloads/gcc-arm-aarch32/bin/arm-none-linux-gnueabihf- scripts

# 如果不增加make modules这一步,在编译ko时可能会找不到Module.symvers文件。参考文章:<https://www.jianshu.com/p/05450481c10e>
# make ARCH=arm CROSS_COMPILE=/home/bling/Downloads/gcc-arm-aarch32/bin/arm-none-linux-gnueabihf- modules

以上,便会生成我们编译ko时所依赖的各种文件。

所以接下来,我们编写ko源码及Makefile文件。

ko源码及Makefile

ko源码 - exp.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/arm-smccc.h>
#include <asm/memory.h>

struct smc_calls_result {
unsigned long arg0;
unsigned long arg1;
unsigned long arg2;
unsigned long arg3;
};

static int __init init_function(void)
{
union{
struct arm_smccc_res smccc;
struct smc_calls_result result;
} res;
printk("hello! a test ko!\\n");

unsigned long a0 = 0xB2000016;
unsigned long a1 = 0x0;
unsigned long a2 = 0x1;
unsigned long a3 = 0x2;
arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, &res.smccc);
printk("hello! end\\n");

return 0;
}

static void __exit exit_function(void)
{
printk("bye bye~\\n");
}

module_init(init_function);
module_exit(exit_function);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("bling");
MODULE_DESCRIPTION("testdriver");

Makefile:

KDIR指定linux源码目录

1
2
3
4
5
6
7
KDIR := /home/bling/Downloads/linux
obj-m += exp.o

all:
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean

编译命令:

1
make ARCH=arm CROSS_COMPILE=/home/bling/Downloads/gcc-arm-aarch32/bin/arm-none-linux-gnueabihf-

最后,将生成的exp.ko传到qemu内,可执行成功!

思考

  1. 换成其他交叉编译器呢?

比如linaro的:https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/

实验证明,更换编译器后也能编译成功。所以重点在于源码版本.config文件

  1. 编译内核源码目录和编译ko使用不同的交叉编译器呢?

不行。实验证明会报如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
$ make ARCH=arm CROSS_COMPILE=/home/bling/Downloads/gcc-arm-aarch32/bin/arm-none-linux-gnueabihf-
make -C /home/bling/Downloads/linux M=/home/bling/optee_v7/kotest modules
make[1]: Entering directory '/home/bling/Downloads/linux'
CC [M] /home/bling/optee_v7/kotest/exp.o
cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: undefined symbol: _Z14rtx_alloc_stat8rtx_code
scripts/Makefile.build:271: recipe for target '/home/bling/optee_v7/kotest/exp.o' failed
make[2]: *** [/home/bling/optee_v7/kotest/exp.o] Error 1
Makefile:1851: recipe for target '/home/bling/optee_v7/kotest' failed
make[1]: *** [/home/bling/optee_v7/kotest] Error 2
make[1]: Leaving directory '/home/bling/Downloads/linux'
Makefile:5: recipe for target 'all' failed
make: *** [all] Error 2

所以,必须保证编译内核源码和编译ko时使用同一个版本交叉编译器。具体原因等以后学内核的时候再深究。

ref

如何为嵌入式开发建立交叉编译环境