构建内核模块时设置包含路径
Setting Include Paths When Building Kernel Modules
我正在尝试为 Linux 编译内核模块。我有以下文件:testuio.c
和 Makefile
。当我键入 make all
时,出现以下错误:
$ make all
make -C /lib/modules/`uname -r`/build M=/srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
CC [M] /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o
In file included from /usr/include/unistd.h:25,
from /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.c:13:
/usr/include/features.h:424:12: fatal error: sys/cdefs.h: No such file or directory
# include <sys/cdefs.h>
^~~~~~~~~~~~~
compilation terminated.
make[3]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/scripts/Makefile.build:271: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:1665: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory] Error 2
make[1]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:179: sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
make: *** [Makefile:19: all] Error 2
没错,/usr/include/sys/下没有这个文件。我不明白的是为什么在 /usr/include/x86_64-linux-gnu/sys 下有这样一个文件的地方找不到它。
以下是gcc -xc -E -v -
输出的一部分:
#include <...> search starts here:
.
/usr/lib/gcc/x86_64-linux-gnu/8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
我的 Makefile 包含:
# (1) consult https://www.kernel.org/doc/Documentation/kbuild/modules.txt to build a kbuild-compatible Makefile
# sections of special interest: 3.1 (shared makefile)
# (2) specifics about the Makefile under https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
# sections of special interest 3.7 (compilation flags)
ifneq ($(KERNELRELEASE),)
# kbuild part of the makefile (could pull this out into a file named Kbuild, this variant is more robust though)
obj-m += testuio.o
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
#ccflags-y = -I/usr/include/aarch64-linux-gnu -I/usr/include # see (2) section 3.7
else
# "normal" makefile
KDIR ?= /lib/modules/`uname -r`/build # ?= sets KDIR only if it has no value already
all:
make -C $(KDIR) M=$(PWD) modules
install:
make -C $(KDIR) M=$(PWD) modules_install
clean:
make -C $(KDIR) M=$(PWD) clean
endif
作为参考,我的 testuio.c 包含:
// In order for a device to be compatible with this UIO platform device driver
// it needs to use "generic-uio" in its compatible property
// This is a kernel driver.
#include <linux/module.h> // included for module_* macros
#include <linux/device.h> // included for devm_kzalloc
#include <linux/mman.h> // included for mmap
// #include <linux/stat.h> // included for fstat (for being able to read out of files)
#include <linux/platform_device.h> // included for struct platform_device
#include <linux/uio_driver.h> // included for struct uio_info
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
#define EOPENUIOFD 1
#define EMEMMAP 2
#define UIO_SIZE_FILE "/sys/class/uio/uio0/maps/map0/size"
MODULE_LICENSE("");
MODULE_AUTHOR("Alexander Pastor");
MODULE_VERSION("0.0.1");
// MODULE_DEVICE_TABLE(???);
typedef struct testuio_dev {
struct uio_info* info;
} testuio_dev;
// TODO: change s.t. interupts can be handled correctly
// HINT: might not be necessary tho
static int testuio_irq_handler(int irq, struct uio_info* info)
{
// if (IRQ is not caused by my hardware)
if (true)
return IRQ_NONE;
/* Disable interrupt */
// Perform some register access to silence the IRQ line
return IRQ_HANDLED;
}
static int testuio_probe(struct platform_device* pdev)
{
struct testuio_dev* dev;
struct resource* res;
int irq;
// collect handles and information from platform device
// devm_kzalloc => managed (free upon detaching device from system) kernel zero-initialized memory allocation
// gfp flags => get free page flags
dev = devm_kzalloc(&pdev->dev, (sizeof(struct testuio_dev)), GFP_KERNEL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
// basic uio info struct initialization (required!)
dev->info->name = "testuio";
dev->info->version = "0.0.1";
// memory region initialization
dev->info->mem[0].name = "dummy_mem";
dev->info->mem[0].addr = res->start;
dev->info->mem[0].size = resource_size(res);
dev->info->mem[0].memtype = UIO_MEM_PHYS;
// other memory types include:
// - UIO_MEM_LOGICAL allocated by kmalloc
// - UIO_MEM_VIRTUAL allocated by vmalloc
// interrupt initialization
dev->info->irq = irq;
dev->info->irq_flags = 0;
dev->info->handler = &testuio_irq_handler;
if(uio_register_device(&pdev->dev, info))
{
iounmap(dev->info->mem[0].internal_addr);
return -ENODEV;
} else {
return 0;
}
}
int main(int argc, char** argv)
{
int uio_fd; // UIO file descriptor
unsigned int uio_size; // memory size of UIO
FILE* size_fp; // pointer to the file containing memory size
void* base_address; // start address of mapped memory
uio_fd = open(/dev/uio, O_RDWR);
if(uio_fd == -1)
return EOPENUIOFD;
size_fp = fopen(UIO_SIZE_FILE, O_RDONLY);
fscanf(size_fp, "0x%08x", &uio_size); // 0x%08x expects unsigned int; %p expects void*
// Whenever the user space program reads or writes in the virtual address range
// it is accessing the device w/o the need for a system call. This improves performance.
base_address = mmap(NULL, // NULL => kernel chooses page-aligned address at which to create mapping
uio_size, // length of memory mapping
PROT_READ | PROT_WRITE, // flags: grant read + write access
MAP_SHARED_VALIDATE, // updates to mapping are visible to other processes
// (+validates given flags, available since Linux 4.15)
uio_fd,
0);
if (uio_fd == MAP_FAILED)
return EMEMMAP;
// --- BEGIN APPLICATION CODE
// ??? Is this even the right spot for the application code
// see also
// https://docplayer.net/37414164-Linux-user-space-device-drivers-john-linn-based-on-3-14-linux-kernel.html
// helpful:
// TODO: read in content from file as done in link above using fstat
int testvals[8] = {0xDEADBEEF, 7, 12, 13, 31, 42, -63, -65535}
memcpy(base_address, testvals, sizeof(testvals));
for(int i=0; i<8; i++)
{
printk(KERN_INFO "The value at address %p is %d",
base_address+i*sizeof(int),
(int)*(base_address+i*sizeof(int)));
}
//! interrupt stuff
// read() returns the number of interrupt events.
// It allows blocking and non-blocking modes with this being blocking mode
int pending = 0;
int reennable = 1;
read(uio_fd, (void*)&pending, sizeof(int));
//! device specific processing
// acking the interrupt in the device
write(uio_fd, (void*)&reenable, sizeof(int));
// --- END APPLICATON CODE
//! undo virtual address mapping
munmap(base_address, uio_size);
return 0;
}
您似乎在代码中混淆了内核和用户space 内容。然而这一切都是错误的:
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
当你拼接一些用户时-space包含你的内核标志的路径。
还有所有这些:
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
不属于内核 module.The main
功能也必须消失。
我的假设是您需要一个内核模块和一个用户space 应用程序来测试它。不要把他们两个混在一起。将这些东西分开。
我正在尝试为 Linux 编译内核模块。我有以下文件:testuio.c
和 Makefile
。当我键入 make all
时,出现以下错误:
$ make all
make -C /lib/modules/`uname -r`/build M=/srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
CC [M] /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o
In file included from /usr/include/unistd.h:25,
from /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.c:13:
/usr/include/features.h:424:12: fatal error: sys/cdefs.h: No such file or directory
# include <sys/cdefs.h>
^~~~~~~~~~~~~
compilation terminated.
make[3]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/scripts/Makefile.build:271: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:1665: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory] Error 2
make[1]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:179: sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
make: *** [Makefile:19: all] Error 2
没错,/usr/include/sys/下没有这个文件。我不明白的是为什么在 /usr/include/x86_64-linux-gnu/sys 下有这样一个文件的地方找不到它。
以下是gcc -xc -E -v -
输出的一部分:
#include <...> search starts here:
.
/usr/lib/gcc/x86_64-linux-gnu/8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
我的 Makefile 包含:
# (1) consult https://www.kernel.org/doc/Documentation/kbuild/modules.txt to build a kbuild-compatible Makefile
# sections of special interest: 3.1 (shared makefile)
# (2) specifics about the Makefile under https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
# sections of special interest 3.7 (compilation flags)
ifneq ($(KERNELRELEASE),)
# kbuild part of the makefile (could pull this out into a file named Kbuild, this variant is more robust though)
obj-m += testuio.o
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
#ccflags-y = -I/usr/include/aarch64-linux-gnu -I/usr/include # see (2) section 3.7
else
# "normal" makefile
KDIR ?= /lib/modules/`uname -r`/build # ?= sets KDIR only if it has no value already
all:
make -C $(KDIR) M=$(PWD) modules
install:
make -C $(KDIR) M=$(PWD) modules_install
clean:
make -C $(KDIR) M=$(PWD) clean
endif
作为参考,我的 testuio.c 包含:
// In order for a device to be compatible with this UIO platform device driver
// it needs to use "generic-uio" in its compatible property
// This is a kernel driver.
#include <linux/module.h> // included for module_* macros
#include <linux/device.h> // included for devm_kzalloc
#include <linux/mman.h> // included for mmap
// #include <linux/stat.h> // included for fstat (for being able to read out of files)
#include <linux/platform_device.h> // included for struct platform_device
#include <linux/uio_driver.h> // included for struct uio_info
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
#define EOPENUIOFD 1
#define EMEMMAP 2
#define UIO_SIZE_FILE "/sys/class/uio/uio0/maps/map0/size"
MODULE_LICENSE("");
MODULE_AUTHOR("Alexander Pastor");
MODULE_VERSION("0.0.1");
// MODULE_DEVICE_TABLE(???);
typedef struct testuio_dev {
struct uio_info* info;
} testuio_dev;
// TODO: change s.t. interupts can be handled correctly
// HINT: might not be necessary tho
static int testuio_irq_handler(int irq, struct uio_info* info)
{
// if (IRQ is not caused by my hardware)
if (true)
return IRQ_NONE;
/* Disable interrupt */
// Perform some register access to silence the IRQ line
return IRQ_HANDLED;
}
static int testuio_probe(struct platform_device* pdev)
{
struct testuio_dev* dev;
struct resource* res;
int irq;
// collect handles and information from platform device
// devm_kzalloc => managed (free upon detaching device from system) kernel zero-initialized memory allocation
// gfp flags => get free page flags
dev = devm_kzalloc(&pdev->dev, (sizeof(struct testuio_dev)), GFP_KERNEL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
// basic uio info struct initialization (required!)
dev->info->name = "testuio";
dev->info->version = "0.0.1";
// memory region initialization
dev->info->mem[0].name = "dummy_mem";
dev->info->mem[0].addr = res->start;
dev->info->mem[0].size = resource_size(res);
dev->info->mem[0].memtype = UIO_MEM_PHYS;
// other memory types include:
// - UIO_MEM_LOGICAL allocated by kmalloc
// - UIO_MEM_VIRTUAL allocated by vmalloc
// interrupt initialization
dev->info->irq = irq;
dev->info->irq_flags = 0;
dev->info->handler = &testuio_irq_handler;
if(uio_register_device(&pdev->dev, info))
{
iounmap(dev->info->mem[0].internal_addr);
return -ENODEV;
} else {
return 0;
}
}
int main(int argc, char** argv)
{
int uio_fd; // UIO file descriptor
unsigned int uio_size; // memory size of UIO
FILE* size_fp; // pointer to the file containing memory size
void* base_address; // start address of mapped memory
uio_fd = open(/dev/uio, O_RDWR);
if(uio_fd == -1)
return EOPENUIOFD;
size_fp = fopen(UIO_SIZE_FILE, O_RDONLY);
fscanf(size_fp, "0x%08x", &uio_size); // 0x%08x expects unsigned int; %p expects void*
// Whenever the user space program reads or writes in the virtual address range
// it is accessing the device w/o the need for a system call. This improves performance.
base_address = mmap(NULL, // NULL => kernel chooses page-aligned address at which to create mapping
uio_size, // length of memory mapping
PROT_READ | PROT_WRITE, // flags: grant read + write access
MAP_SHARED_VALIDATE, // updates to mapping are visible to other processes
// (+validates given flags, available since Linux 4.15)
uio_fd,
0);
if (uio_fd == MAP_FAILED)
return EMEMMAP;
// --- BEGIN APPLICATION CODE
// ??? Is this even the right spot for the application code
// see also
// https://docplayer.net/37414164-Linux-user-space-device-drivers-john-linn-based-on-3-14-linux-kernel.html
// helpful:
// TODO: read in content from file as done in link above using fstat
int testvals[8] = {0xDEADBEEF, 7, 12, 13, 31, 42, -63, -65535}
memcpy(base_address, testvals, sizeof(testvals));
for(int i=0; i<8; i++)
{
printk(KERN_INFO "The value at address %p is %d",
base_address+i*sizeof(int),
(int)*(base_address+i*sizeof(int)));
}
//! interrupt stuff
// read() returns the number of interrupt events.
// It allows blocking and non-blocking modes with this being blocking mode
int pending = 0;
int reennable = 1;
read(uio_fd, (void*)&pending, sizeof(int));
//! device specific processing
// acking the interrupt in the device
write(uio_fd, (void*)&reenable, sizeof(int));
// --- END APPLICATON CODE
//! undo virtual address mapping
munmap(base_address, uio_size);
return 0;
}
您似乎在代码中混淆了内核和用户space 内容。然而这一切都是错误的:
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
当你拼接一些用户时-space包含你的内核标志的路径。
还有所有这些:
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
不属于内核 module.The main
功能也必须消失。
我的假设是您需要一个内核模块和一个用户space 应用程序来测试它。不要把他们两个混在一起。将这些东西分开。