linux驱动编译系统

来源:互联网 发布:python 有序字典 编辑:程序博客网 时间:2024/06/06 10:41

前言

从Linux内核2.6开始,Linux内核的编译采用Kbuild系统,这同过去的编译系统有很大的不同,尤其对于Linux内核模块的编译。在新的系统下,Linux编译系统会多次扫描Linux的Makefile。

范列

下面我们通过一个简单的驱动示例,来熟悉linux的编译系统,驱动代码如下:

/** test.c -- the example of printf "hello world!" in the screen of driver program*/#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");/* declare the license of the module ,it is necessary */static int hello_init(void){    printk(KERN_EMERG "Hello World enter!\n");    return 0;}static void hello_exit(void){    printk(KERN_EMERG "Hello world exit!\n");}module_init(hello_init); /* load the module */module_exit(hello_exit); /* unload the module *//* before is some decription of the model,not necessary */MODULE_AUTHOR("Jalon Xiao");MODULE_DESCRIPTION("This is an example of programming driver!");

Makefile源码如下:

ifneq ($(KERNELRELEASE),) $(warning A top-level warning)obj-m := test.o else$(warning A Bottom-level waring).PHONY: modules cleanKERNELDIR =/usr/src/linux-headers-3.2.0-24-genericPWD := $(shell pwd)modules:    $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versionsendif

将上面的源码保存在同一目录下,在命令终端运行make可以编译test模块,linux编译系统会编译生成test.ko内核模块。
编译截图
从上面的编译截图可以看出内核编译系统会两次读取编译模块makefile。后面的打印A top-level warning都为内核编译系统的读取,第一次为make读取。make在执行读取并执行Makefile时,由于没有定义KERNELRELEASE.会执行else后面的目标,第一个目标modules为默认目标。 (MAKE)C(KERNELDIR) M=(PWD)modules(KERNELDIR)目录执行顶层makefile,M=$(PWD) 是参数,modules 是目标。随后由内核编译系统根据参数再次读取模块makefile,由于编译的内核都已经定义了KERNELRELEASE,所以实质上内核编译系统仅仅只读取了obj-m := test.o ,随后根据kbuild的编译规则编译内核模块。

Kbuild的编译目标

obj-m和obj-y都是Kbuild编译系统的目标。
列如obj-y += foo.o,它告诉kbuild在当前目录下,有一个叫做foo.o的目标文件,它将从foo.c或则foo.S编译得到。
或者obj-m += foo.o,同样告诉kbuild在当前目录下,有一个叫做foo.o的目标文件,它将从foo.c或则foo.S编译得到。
不同的是Kbuild编译完所有的(objy)(LD) -r”把所有这些文件合并到built-in.o文件。这个built-in.o会被上一级目录的Makefile使用,最终链接到vmlinux中。而obj-m编译成ko模块。
最简单的kbuild Makefile可以仅包含:

obj-$(CONFIG_FOO) += foo.o

其中CONFIG_FOO可以等于y或m,它的值由.config文件给出。如果$(CONFIG_FOO)既不是y也不是m,那么该文件不会被编译和链接.

如果一个驱动模块不是由一个源文件编写时,makefile应按照如下规则进行,否则会出现

/work/mtkv1.33/kernel/drivers/net/wireless/DPO_RT5572_LinuxSTA_2.6.0.1_20120629/common/crypt_sha2.c:549:1: fatal error: opening dependency file drivers/net/wireless/DPO_RT5572_LinuxSTA_2.6.0.1_20120629/common/.crypt_sha2.o.d: No such file or directorycompilation terminated.

类似的错误!

所以当某个目标由多个源文件编译得到时,可以通过$(<module_name>-objs)或$(<module_name>-y)把这些源文件告诉kbuild。Kbuild能够识别后缀-objs和-y/-m,例如:

#drivers/isdn/i4l/Makefile         obj-$(CONFIG_ISDN) += isdn.o         isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

Kbuild会编译所有$(isdn-objs)中的对象,并调用"$(LD) -r"把它们链接成isdn.o文件。

Kbuild编译选项变量

Makefile里常用的编译选项,参见内核目录:Document/kbuild/modules.txt和Document/kbuild/makefiles.txt。
ccflags-y、asflags-y和ldflags-y分别对应EXTRA_CFLAGS、EXTRA_AFLAGS和EXTRA_LDFLAGS三个,分别用于$(CC)、$(AS)、$(LD)。这三个变量只在当前Makefile中有效。
为驱动模块添加外部头文件的方法:在文件Kbuild里通过内核关键字EXTRA_CFLAGS来指定。具体为:

EXTRA_CFLAGS += -I$(EXTERNAL_INC) -I$(LOCAL_INC)
# file dev_var_def.mkPWD := $(shell pwd)EXTRERNAL_INC := $(PWD)/../includeLOCAL_INC := $(PWD)

另外还有subdir-ccflags-y、subdir-asflags-y这两个变量作用于当前Makefile及其所有子目录。
这里只介绍了常用的编译选项,其他的参考内核文档。

Kbuild调用子目录Makefile

Makefile只负责编译当前目录中的对象。子目录中的对象,由子目录中的Makefile负责。如何让make调用子目录中的Makefile?答案是把子目录包含到obj-y或obj-m中。例如:

  #fs/Makefile         obj-$(CONFIG_EXT2_FS) += ext2/

Kbuild lib库对象

在一个目录下,obj-y所列出的文件,将被编译成built-in.o文件,而lib-y或lib-m所列出的文件,将在当前目录下生成lib.a文件。
注意:一般lib-y或lib-m只用在lib/和arch/*/lib这两个目录中。

较全面的makefile模板

MODULE_NAME=test#####################in order to unify with the make menuconfig,should define to CONFIG_$(MODULE_NAME)MODULE_CONFIG=CONFIG_TEST #############IF not config by make menuconfig,should define MODULE_CONFIG#define m to compile MODULE_NAME.ko#CONFIG_TEST=m  CROSS_CONFIG=nDEBUG = yifneq ($(KERNELRELEASE),) #########################kbuild system start to compile$(warning A top-level warning)# Add your debugging flag (or not) to CFLAGSifeq ($(DEBUG),y)    DEBFLAGS = -O -g -DDEBUG # "-O" is needed to expand inlineselse    DEBFLAGS = -O2endifEXTRA_LDFLAGS += $(DEBFLAGS) #ccflags-y#obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o  #why no ok?????obj-m := $(MODULE_NAME).o#for Multi-files module#$(MODULE_NAME)-objs +=  else########################first read this makefile$(warning A Bottom-level waring)#######################define the compile toolsifeq ($(CROSS_CONFIG), y)#for Cross-compileKERNELDIR =/home/xiaojingling/work/mtkv1.33/kernelARCH = arm#FIXME:maybe we need absolute path for different user. eg root#CROSS_COMPILE = arm-none-linux-gnueabi-CROSS_COMPILE = INSTALLDIR := $(shell pwd)else#for Local compileKERNELDIR = /usr/src/linux-headers-3.2.0-24-genericARCH = x86#only need the prefix of the tools,similar to arm compiling toolsCROSS_COMPILE =   INSTALLDIR := ./endif########################end of defining the toolsPWD := $(shell pwd).PHONY: modules cleanmodules:    $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modulesclean:    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versionsendif

问题:

#obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o  #why no ok?????这样定义的时候居然不能编译目标,不知道哪里出了问题。若这样定义成功可以方便集成到kernel内部。obj-m := $(MODULE_NAME).o

需要编译其他的模块时,更具实际情况修改如下值就可以了:

MODULE_NAME=test #目标名CROSS_CONFIG=n #交叉编译时为yDEBUG = y #调试选项

关于上面的makefile更详细的请参考Linux内核模块LKM编译-自制Makefile模板

参考文档:
Linux 2.6内核Makefile浅析
Linux内核模块LKM编译-自制Makefile模板
Makefile编译选项
Android 驱动之旅

0 0
原创粉丝点击