0

As a learning exercise, I'm trying to build a kernel object file from a c file which would only do printk("hello, world") when loaded with modprobe. My kernel version is 4.15.

So let's say I have this example code:

int printk(const char *fmt, ...);

struct platform_device;

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
};

static struct platform_driver my_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove
};

module_platform_driver(my_driver);

static int my_driver_probe(struct platform_device *pdev) {
        printk("my driver loaded\n");
    return 0;
}

static int my_driver_remove(struct platform_device *pdev) {
        printk("my driver removed\n");
    return 0;
}

How do I compile it using gcc to create a valid my_driver.ko file that can be loaded with modprobe?

sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • Copy the standard makefile, run it, and note all `gcc` commands that it runs. – KamilCuk Oct 17 '21 at 12:51
  • @ServeLaurijssen did you even read my question? I specifically do not want to use kernel sources or headers and I've explained why inside. My question is not a duplicate of that one, unless you read only the title. – sashoalm Oct 17 '21 at 12:58
  • It's probably possible, copying a bunch of stuff from the kernel headers into your module including the linker script that is used for modules... at that point might just well do it the right way. – Marco Bonelli Oct 17 '21 at 13:08
  • 3
    What your question "Is it possible to do it that way?" **actually** means? What kind of **answer** do you **expect**? If you want an answer to the literal question, then "Yes, it is possible to build the kernel module file without kernel sources and headers". If you want to know the exact steps, then the question is too broad: 1. Compiler flags are specific for kernel configuration. You can find them via `make V=1`. 2. Post processing of the object file includes e.g. inserting `THIS_MODULE` definition of type `struct module`. Its fields depends from the kernel configuration too. – Tsyvarev Oct 17 '21 at 14:45
  • There are no blackbox scripts or headers in the kernel. All of the source is available. – stark Oct 19 '21 at 03:02
  • @Tsyvarev My question is what are the compiler flags to use in order to do it. If the compiler flags are version-specific, then just for my version which is 4.15. – sashoalm Oct 19 '21 at 08:03
  • 2
    As I said above, compiler flags are **configuration**-specific: they depend from what you enter while configure the kernel with `make menuconfig` or similar. If you want to find out compiler flags for your **specific kernel build**, then just create dummy module and build it with `make V=1`: all compiler flags will be in the output. – Tsyvarev Oct 19 '21 at 08:30
  • This isn't a simple as a set of commands. Kernel modules have very specific binary formats and linker script requirements etc. I'd highly suggest taking a look at the docs for it: https://www.kernel.org/doc/html/latest/kbuild/modules.html – Mgetz Oct 19 '21 at 14:33
  • @Tsyvarev thank you for the suggestions. I have edited my question and I think it is more clear now. Could you take a look at it to see if it is OK? Also I've added a bounty to it. – sashoalm Oct 23 '21 at 09:16
  • You _need_ kernel headers for module_platform_driver. `my_driver_probe` is undefined at point of use. I do not understand your question - so copy the makefile and record all gcc commands, why not? – KamilCuk Oct 23 '21 at 09:17

1 Answers1

1

I'm trying to build a kernel object file from a c file which would only do printk("hello, world")

A short google search for such module resulted for me in the following examples: https://github.com/maK-/SimplestLKM https://github.com/moutoum/linux-kernel-module-hello-world https://github.com/ichergui/hello-world https://github.com/carloscdias/hello-world-linux-module .

How do I compile it using gcc to create a valid my_driver.ko file that can be loaded with modprobe?

The first project has the following source for a hello world module saved in hello.c:

#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("maK");

static int hello_init(void){
    printk(KERN_ALERT "Hello world!\n");
    return 0;
}

static void hello_exit(void){
    printk(KERN_ALERT "Goodbye cruel world\n");
}

module_init(hello_init);
module_exit(hello_exit);

The following is a transcript of a session with compiling the first project with V=1:

$ git clone https://github.com/maK-/SimplestLKM
Cloning into 'SimplestLKM'...
remote: Enumerating objects: 15, done.
remote: Total 15 (delta 0), reused 0 (delta 0), pack-reused 15
Receiving objects: 100% (15/15), 13.47 KiB | 431.00 KiB/s, done.
Resolving deltas: 100% (4/4), done.
$ cd SimplestLKM/
$ make V=1
make -C /lib/modules/5.10.72-1-lts/build M=/dev/shm/.1000.home.tmp.dir/SimplestLKM modules
make[1]: Entering directory '/usr/lib/modules/5.10.72-1-lts/build'
test -e include/generated/autoconf.h -a -e include/config/auto.conf || (        \
echo >&2;                           \
echo >&2 "  ERROR: Kernel configuration is invalid.";       \
echo >&2 "         include/generated/autoconf.h or include/config/auto.conf are missing.";\
echo >&2 "         Run 'make oldconfig && make prepare' on kernel src to fix it.";  \
echo >&2 ;                          \
/bin/false)
make -f ./scripts/Makefile.build obj=/dev/shm/.1000.home.tmp.dir/SimplestLKM \
single-build= \
need-builtin=1 need-modorder=1
  gcc -Wp,-MMD,/dev/shm/.1000.home.tmp.dir/SimplestLKM/.hello.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include -I./arch/x86/include -I./arch/x86/include/generated  -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 -fno-allow-store-data-races -Wframe-larger-than=2048 -fstack-protector-strong -Wimplicit-fallthrough -Wno-unused-but-set-variable -Wno-unused-const-variable -g -gdwarf-4 -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-stringop-truncation -Wno-zero-length-bounds -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wno-packed-not-aligned -fplugin=./scripts/gcc-plugins/structleak_plugin.so -fplugin-arg-structleak_plugin-byref-all -DSTRUCTLEAK_PLUGIN  -DMODULE  -DKBUILD_BASENAME='"hello"' -DKBUILD_MODNAME='"hello"' -c -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.c
   ./tools/objtool/objtool orc generate  --module --no-fp --retpoline --uaccess /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.o
  { echo  /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.o;  echo; } > /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod
  {   echo /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.ko; :; } | awk '!x[$0]++' - > /dev/shm/.1000.home.tmp.dir/SimplestLKM/modules.order
make -f ./scripts/Makefile.modpost
  sed 's/ko$/o/' /dev/shm/.1000.home.tmp.dir/SimplestLKM/modules.order | scripts/mod/modpost  -a   -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/Module.symvers -e -i Module.symvers  -N -T -
make -f ./scripts/Makefile.modfinal
  gcc -Wp,-MMD,/dev/shm/.1000.home.tmp.dir/SimplestLKM/.hello.mod.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include -I./arch/x86/include -I./arch/x86/include/generated  -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 -fno-allow-store-data-races -Wframe-larger-than=2048 -fstack-protector-strong -Wimplicit-fallthrough -Wno-unused-but-set-variable -Wno-unused-const-variable -g -gdwarf-4 -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-stringop-truncation -Wno-zero-length-bounds -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wno-packed-not-aligned -fplugin=./scripts/gcc-plugins/structleak_plugin.so -fplugin-arg-structleak_plugin-byref-all -DSTRUCTLEAK_PLUGIN  -DMODULE  -DKBUILD_BASENAME='"hello.mod"' -DKBUILD_MODNAME='"hello"' -c -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod.o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod.c
  ld -r -m elf_x86_64 --build-id=sha1  -T scripts/module.lds -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.ko /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod.o;  true
make[1]: Leaving directory '/usr/lib/modules/5.10.72-1-lts/build'

From that we know that we also need the generated hello.mod.c. The content for this module as generated by modpost program is as follows:

#include <linux/module.h>
#define INCLUDE_VERMAGIC
#include <linux/build-salt.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>

BUILD_SALT;

MODULE_INFO(vermagic, VERMAGIC_STRING);
MODULE_INFO(name, KBUILD_MODNAME);

__visible struct module __this_module
__section(".gnu.linkonce.this_module") = {
    .name = KBUILD_MODNAME,
    .init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
    .exit = cleanup_module,
#endif
    .arch = MODULE_ARCH_INIT,
};

#ifdef CONFIG_RETPOLINE
MODULE_INFO(retpoline, "Y");
#endif

MODULE_INFO(depends, "");


MODULE_INFO(srcversion, "C7C2D304485DDC1C93263AE");

With both files in place, we can compile them with gcc, first to hello.o and hello.mod.o and then combine with ld. So these are the compiler flags needed to compile the module on my system:

( cd /usr/lib/modules/5.10.72-1-lts/build/ && gcc -nostdinc -isystem /usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include -I./arch/x86/include -I./arch/x86/include/generated  -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 -fno-allow-store-data-races -Wframe-larger-than=2048 -fstack-protector-strong -Wimplicit-fallthrough -Wno-unused-but-set-variable -Wno-unused-const-variable -g -gdwarf-4 -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-stringop-truncation -Wno-zero-length-bounds -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wno-packed-not-aligned -fplugin=./scripts/gcc-plugins/structleak_plugin.so -fplugin-arg-structleak_plugin-byref-all -DSTRUCTLEAK_PLUGIN  -DMODULE  -DKBUILD_BASENAME='"hello"' -DKBUILD_MODNAME='"hello"' -c -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.c )

( cd /usr/lib/modules/5.10.72-1-lts/build/ && gcc -nostdinc -isystem /usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include -I./arch/x86/include -I./arch/x86/include/generated  -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 -fno-allow-store-data-races -Wframe-larger-than=2048 -fstack-protector-strong -Wimplicit-fallthrough -Wno-unused-but-set-variable -Wno-unused-const-variable -g -gdwarf-4 -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-stringop-truncation -Wno-zero-length-bounds -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wno-packed-not-aligned -fplugin=./scripts/gcc-plugins/structleak_plugin.so -fplugin-arg-structleak_plugin-byref-all -DSTRUCTLEAK_PLUGIN  -DMODULE  -DKBUILD_BASENAME='"hello"' -DKBUILD_MODNAME='"hello"' -c -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod.o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod.c )

( cd /usr/lib/modules/5.10.72-1-lts/build/ && ld -r -m elf_x86_64 --build-id=sha1  -T scripts/module.lds -o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.ko /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.o /dev/shm/.1000.home.tmp.dir/SimplestLKM/hello.mod.o )

After that we can insmod hello.ko and have hello world printed with CRIT in dmesg.

what are the compiler flags to use in order to do it.

In the same fashion as presented above, you can find out what options are passed to the compiler on your system.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111