Cómo compilar un módulo de kernel cargable sin volver a compilar kernel

20

He leído bastante sobre cómo compilar un módulo de kernel en (y para) la Raspberry Pi, pero todavía no puedo entender por qué no funciona. He podido construir el módulo, pero informa Invalid module formatcuando intento insmodel resultado. Aquí está el proceso que seguí. Primero, como root debajo /rootejecuté el siguiente script de shell:

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 

Las primeras líneas son de http://lostindetails.com/blog/post/Compiling-a-kernel-module-for-the-raspberry-pi-2

El resto lo escribí para automatizar más el proceso. Una vez que todo se ejecuta correctamente, tengo la fuente que debe coincidir exactamente con el núcleo en ejecución, la configuración para que coincida y un enlace simbólico. Hubo algunos redireccionamientos desde la ubicación web de github (aparentemente ahora es https://raw.githubusercontent.com/ ) pero no hay errores reales.

Luego me convierto en el piusuario predeterminado y en un directorio llamado /home/pi/projects/lkmTengo este código fuente para un módulo de juguete muy simple:

Hola 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);

Finalmente, construyo el módulo con este 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

Finalmente, intento cargar el módulo:

sudo insmod hello.ko

El resultado, sin embargo, es decepcionante:

insmod: ERROR: no se pudo insertar el módulo hello.ko: formato de módulo no válido

Posiblemente detalles relevantes

Estoy usando la última jessieversión actual de Raspbian en una 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) 

Desafortunadamente, no estoy seguro de cómo solucionar este problema o solucionarlo. ¿Alguna pista?

Eduardo
fuente
Recopilé todos mis hallazgos y experiencias en un script, consulte github.com/x29a/kernel/blob/master/rpi/prepare.sh y el blogpost blog.chris007.de/…
x29a

Respuestas:

23

En primer lugar, asegúrese de usar los encabezados de kernel adecuados. Supongo que los encabezados del núcleo y el código fuente están más actualizados que el núcleo que está ejecutando.

Intente hacer y apt-get update && apt-get upgradeluego reinstale el módulo. Si el problema persiste, verifique tres veces que los encabezados de su kernel coincidan con su kernel actual, vuelva a compilar e intente instalar.


Nota: estoy usando Jessie.

ACTUALIZACIÓN: Ejecútelos como root.

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

# Update the kernel!
rpi-update

Es posible que deba reiniciar. Después de eso, continúe con los comandos a continuación, aún utilizando la cuenta raíz.

# 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-sourcearroja un error de GCC (algo acerca de una discrepancia de versión), está bien siempre que su versión actual de GCC sea más alta . Correr en rpi-source --skip-gcclugar derpi-source

Luego, continúe con su ejemplo de Hello World. Crea la carpeta y cden ella. Luego, crea los archivos.

mkdir hello
cd hello

Archivos:

Hola 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 (¿ distingue entre mayúsculas y minúsculas?)

obj-m := hello.o

Ahora que tiene sus archivos, puede continuar y ejecutar los comandos de compilación habituales de Hello World:

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

Ahora deberías comprobarlo dmesg. La última línea debe imprimirse Hello World :)resaltada en rojo.

Si lo haces, felicidades. Acaba de crear e instalar un módulo de kernel.

Ahora quítelo usando rmmod hello. dmesgahora debería imprimirse Goodbye World!resaltado en rojo.

Fuentes: 1 2 3

PNDA
fuente
Cuando dice "verificar que los encabezados de su núcleo coincidan con su núcleo actual", ¿cómo quiere decir exactamente que debo hacer eso?
Edward
@Edward actualizado.
PNDA
@Edward Tenga en cuenta que este es el ejemplo de hello world. Construí tu módulo, pero me di cuenta de que es lo mismo. La única diferencia es que su código no tiene el resaltado rojo.
PNDA
@Edward En su caso, creo que seguir las instrucciones hasta que la rpi-sourceparte sea suficiente. Puedes intentar construir el tuyo desde ese punto.
PNDA
5

Aquí hay una versión mucho más simple, probada en jessie y stretch .

sudo apt-get install raspberrypi-kernel-headers

y luego cuando sus archivos estén en su lugar:

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

Ejemplo

Crea el hellodirectorio, entra y crea los siguientes archivos: hello.cy Makefile.

Yo recomiendo trabajando como usuario normal, no tiene raíz , solamente insmod, rmmody make modules_installlos comandos requieren permisos de root, y lo necesario sudose muestra en los siguientes comandos.


hello.c (sin cambios, su archivo)

#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 (modificado)

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)   

Uso

  • Compilación: make(en el mismo directorio que el Makefile)
  • Prueba
    • Inserte el módulo con sudo insmod hello.ko
    • Encuentra Hello World :)en la salida dedmesg
    • Retire el módulo con sudo rmmod hello
    • Encuentra Goodbye, world.int la salida dedmesg
  • Instalar, cuando su módulo esté funcionando, sudo make modules_installinstalará el módulo donde pertenece, por modprobelo que funcionará.
pim
fuente
1
funciona muy bien para los núcleos instalados mediante el paquete 'raspberrypi-kernel'. Contrariamente a eso, la descripción emitida por 'pandalion98' se refiere a los núcleos instalados mediante 'rpi-update'. Ambos métodos son mutuamente excluyentes, ¿verdad?
sparkie
1
Creo que esta es una respuesta válida ya que OP (Edward) nunca habló rpi-update, rpi-updatese sugirió en la respuesta de pandalion98
pim
@sparkie En el momento de la publicación, el núcleo todavía no estaba integrado en el aptrepositorio de Raspbian , si no me equivoco. Actualizar el núcleo significaba ejecutar el rpi-updatescript de Hexxeh . En estos días, la actualización raspberrypi-kernelo la ejecución rpi-updatehacen más o menos lo mismo.
PNDA
En cuanto a raspberrypi-kernel-headers, generalmente instala encabezados de kernel no coincidentes, por experiencia (los encabezados tienden a ser una versión más nueva que el kernel), de ahí por qué opté por "ir a manual".
PNDA
parece haber alguna diferencia entre 'raspberrypi-kernel' y 'rpi-update': uno da como resultado '4.9.66+' y el otro en '4.9.59+' en este momento. Así que creo que aún tenemos que manejar ambos procedimientos de compilación por separado
sparkie
2

en el getKernel.sharchivo agregar

sudo modprobe configs

antes de

zcat /proc/config.gz >.config

(ahora en la imagen rpi predeterminada /proc/config.gz no existe)

Igor Nikolaev
fuente