Comment compiler un module noyau chargeable sans recompiler le noyau

20

J'ai lu pas mal de choses sur la façon de compiler un module de noyau sur (et pour) le Raspberry Pi, mais je ne suis toujours pas tout à fait capable de comprendre pourquoi cela ne fonctionne pas. J'ai pu construire le module, mais il rend compte Invalid module formatlorsque j'essaie de trouver insmodle résultat. Voici le processus que j'ai suivi. Tout d'abord, en tant que root sous /rootj'ai exécuté le script shell suivant:

getKernel.sh

#! /usr/bin/bash
FIRMWARE_HASH=$(zgrep "* firmware as of" /usr/share/doc/raspberrypi-bootloader/changelog.Debian.gz | head -1 | awk '{ print $5 }')
KERNEL_HASH=$(wget https://raw.githubusercontent.com/raspberrypi/firmware/$FIRMWARE_HASH/extra/git_hash -O -)
git clone https://github.com/raspberrypi/linux 
cd linux
git checkout $KERNEL_HASH
wget https://raw.githubusercontent.com/raspberrypi/firmware/$FIRMWARE_HASH/extra/Module.symvers 
zcat /proc/config.gz >.config
make oldconfig
make modules_prepare
ln -s /root/linux /lib/modules/$(uname -r)/build 

Les premières lignes proviennent de http://lostindetails.com/blog/post/Compiling-a-kernel-module-for-the-raspberry-pi-2

Le reste, j'ai écrit pour automatiser davantage le processus. Une fois que tout cela fonctionne correctement, j'ai la source qui doit correspondre exactement au noyau en cours d'exécution, la configuration à faire correspondre et un lien symbolique. Il y a eu quelques redirections depuis l'emplacement Web de github (apparemment c'est maintenant https://raw.githubusercontent.com/ ) mais aucune erreur réelle.

Ensuite, je deviens l' piutilisateur par défaut et dans un répertoire nommé /home/pi/projects/lkmj'ai ce code source pour un module jouet très simple:

Bonjour c

#include <linux/init.h>  
#include <linux/kernel.h> 
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Do-nothing test driver");
MODULE_VERSION("0.1");

static int __init hello_init(void){
   printk(KERN_INFO "Hello, world.\n");
   return 0;
}

static void __exit hello_exit(void){
   printk(KERN_INFO "Goodbye, world.\n");
}

module_init(hello_init);
module_exit(hello_exit);

Enfin, je construis le module avec ce Makefile

Makefile

MODSRC=/home/pi/projects/lkm
obj-m+=hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=${MODSRC} modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=${MODSRC} clean

Enfin, j'essaie de charger le module:

sudo insmod hello.ko

Le résultat est cependant décevant:

insmod: ERREUR: impossible d'insérer le module hello.ko: format de module non valide

Détails éventuellement pertinents

J'utilise actuellement la dernière jessieversion de Raspbian sur un Raspberry Pi2.

$ uname --kernel-release --kernel-version
4.1.13-v7+ #826 SMP PREEMPT Fri Nov 13 20:19:03 GMT 2015
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.9/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Raspbian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libitm --disable-libquadmath --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-armhf/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-armhf --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-armhf --with-arch-directory=arm --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-sjlj-exceptions --with-arch=armv6 --with-fpu=vfp --with-float=hard --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
Thread model: posix
gcc version 4.9.2 (Raspbian 4.9.2-10) 

Malheureusement, je ne sais pas comment résoudre ce problème ou le corriger. Des indices?

Edward
la source
J'ai compilé toutes mes découvertes et expériences dans un script, voir github.com/x29a/kernel/blob/master/rpi/prepare.sh et le blogpost correspondant blog.chris007.de/…
x29a

Réponses:

23

Tout d'abord, assurez-vous d'utiliser les en-têtes de noyau appropriés. Je suppose que vos en-têtes de noyau et votre code source sont plus mis à jour que le noyau que vous utilisez.

Essayez de faire apt-get update && apt-get upgradepuis réinstallez le module. Si le problème persiste, vérifiez que vos en-têtes de noyau correspondent à votre noyau actuel, recompilez à nouveau puis essayez d'installer.


Remarque: j'utilise Jessie.

MISE À JOUR: exécutez-les en tant que root.

# The usual update routine
apt-get update -y
apt-get upgrade -y

# Update the kernel!
rpi-update

Vous devrez peut-être redémarrer. Après cela, continuez avec les commandes ci-dessous, toujours en utilisant le compte root.

# Get rpi-source
sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source

# Make it executable
sudo chmod +x /usr/bin/rpi-source

# Tell the update mechanism that this is the latest version of the script
/usr/bin/rpi-source -q --tag-update

# Get the kernel files thingies.
rpi-source

Si rpi-sourcejette une erreur GCC (quelque chose à propos d'une incompatibilité de version), ce n'est pas grave tant que votre version GCC actuelle est supérieure . Exécuter rpi-source --skip-gccau lieu derpi-source

Ensuite, passez à votre exemple Hello World. Créez le dossier et cddedans. Créez ensuite les fichiers.

mkdir hello
cd hello

Des dossiers:

Bonjour c

#include <linux/module.h>
#include <linux/kernel.h>

int hello_init(void)
{
    pr_alert("Hello World :)\n");
    return 0;
}
void hello_exit(void)
{
    pr_alert("Goodbye World!\n");
}
module_init(hello_init);
module_exit(hello_exit);

Makefile (sensible à la casse?)

obj-m := hello.o

Maintenant que vous avez vos fichiers, vous pouvez continuer et exécuter les commandes de construction Hello World habituelles:

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
insmod hello.ko

Vous devriez maintenant vérifier dmesg. La dernière ligne doit s'imprimer en Hello World :)surbrillance rouge.

Si vous le faites, félicitations. Vous venez de créer et d'installer un module noyau.

Maintenant, supprimez-le en utilisant rmmod hello. dmesgdevrait maintenant imprimer Goodbye World!surligné en rouge.

Sources: 1 2 3

PNDA
la source
Lorsque vous dites de "vérifier que vos en-têtes de noyau correspondent à votre noyau actuel", comment voulez-vous dire exactement que je devrais faire cela?
Edward
@Edward mis à jour.
PNDA
@Edward Notez qu'il s'agit de l'exemple du monde bonjour. J'ai construit votre module, mais j'ai réalisé que c'est la même chose. La seule différence est que votre code n'a pas la surbrillance rouge.
PNDA
@Edward Dans votre cas, je pense que suivre les instructions jusqu'à ce que la rpi-sourcepièce soit suffisante. Vous pouvez essayer de construire le vôtre à partir de ce point.
PNDA
5

Il existe une version beaucoup plus simple ici, testé sur jessie et extensible .

sudo apt-get install raspberrypi-kernel-headers

puis lorsque vos fichiers sont en place:

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

Exemple

Créez le hellorépertoire, allez à l'intérieur et créez les fichiers suivants: hello.cet Makefile.

Je recommande de travailler en tant qu'utilisateur normal, et non root , seulement insmod, rmmodet les make modules_installcommandes nécessitent des autorisations root, et le nécessaire sudoest indiqué dans les commandes suivantes.


bonjour.c (inchangé, votre fichier)

#include <linux/init.h>  
#include <linux/kernel.h> 
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Do-nothing test driver");
MODULE_VERSION("0.1");

static int __init hello_init(void){
   printk(KERN_INFO "Hello, world.\n");
   return 0;
}

static void __exit hello_exit(void){
   printk(KERN_INFO "Goodbye, world.\n");
}

module_init(hello_init);
module_exit(hello_exit);

Makefile (modifié)

obj-m+=hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(pwd) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(pwd) clean

modules_install: all
    $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
    $(DEPMOD)   

Usage

  • Build: make(dans le même répertoire que le Makefile)
  • Tester
    • Insérez le module avec sudo insmod hello.ko
    • Rechercher Hello World :)dans la sortie dedmesg
    • Retirez le module avec sudo rmmod hello
    • Rechercher dans Goodbye, world.la sortie dedmesg
  • Installer, lorsque votre module fonctionne, sudo make modules_installinstallera le module à sa place, ainsi modprobefonctionnera.
pim
la source
1
fonctionne très bien pour les noyaux installés au moyen du paquet 'raspberrypi-kernel'. Contrairement à ce que la description émise par «pandalion98» fait référence aux noyaux installés au moyen de «rpi-update». Les deux méthodes sont mutuellement exclusives non?
sparkie
1
Je pense que c'est une réponse valable car OP (Edward) n'a jamais parlé rpi-update, a rpi-updateété suggéré dans la réponse de
pandalion98
@sparkie Au moment de la publication, le noyau n'était toujours pas intégré au aptréférentiel de Raspbian , si je ne me trompe pas. Mettre à jour le noyau signifiait exécuter le rpi-updatescript d'Hexxeh . De nos jours, la mise à jour raspberrypi-kernelou l'exécution rpi-updatefont à peu près la même chose.
PNDA
Quant à raspberrypi-kernel-headers, il installe généralement des en-têtes de noyau incompatibles, par expérience (les en-têtes ont tendance à être une version plus récente que le noyau), d'où la raison pour laquelle j'ai choisi de "passer au manuel".
PNDA
il semble qu'il y ait une certaine différence entre 'raspberrypi-kernel' et 'rpi-update': l'un résulte en '4.9.66+' l'autre en '4.9.59+' en ce moment. Je pense donc que nous devons encore gérer les deux procédures de construction séparément
sparkie
2

dans le getKernel.shfichier ajouter

sudo modprobe configs

avant

zcat /proc/config.gz >.config

(maintenant dans l'image rpi par défaut /proc/config.gz n'existe pas)

Igor Nikolaev
la source