Search code examples
clinux-kernelkernel-module

Build .ko file from .c file using GCC command line


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?


Solution

  • 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.