Como compilar um módulo do kernel carregável sem recompilar o kernel

20

Eu li bastante sobre como compilar um módulo de kernel no (e para) o Raspberry Pi, mas ainda não sou capaz de descobrir por que ele não está funcionando. Consegui construir o módulo, mas ele relata Invalid module formatquando tento insmodo resultado. Aqui está o processo que segui. Primeiro, como root /root, executei o seguinte 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 

As primeiras linhas são de http://lostindetails.com/blog/post/Compiling-a-kernel-module-for-the-raspberry-pi-2

O resto eu escrevi para automatizar mais o processo. Depois que tudo isso for executado com êxito, eu tenho a fonte que deve corresponder exatamente ao kernel em execução, a configuração a ser correspondida e um link simbólico. Houve alguns redirecionamentos do local da web do github (aparentemente agora é https://raw.githubusercontent.com/ ), mas não há erros reais.

Então eu me torno o piusuário padrão e, em um diretório chamado /home/pi/projects/lkm, tenho esse código-fonte para um módulo de brinquedo muito simples:

hello.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, eu construo o módulo com 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

Por fim, tento carregar o módulo:

sudo insmod hello.ko

O resultado, no entanto, é decepcionante:

insmod: ERRO: não foi possível inserir o módulo hello.ko: formato de módulo inválido

Detalhes possivelmente relevantes

Estou usando a jessieversão mais recente do Raspbian em um 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) 

Infelizmente, não tenho certeza de como solucionar mais esse problema ou corrigi-lo. Alguma pista?

Edward
fonte
Compilei todas as minhas descobertas e experiências em um script, consulte github.com/x29a/kernel/blob/master/rpi/prepare.sh e o blogpost relacionado blog.chris007.de/…
x29a

Respostas:

23

Primeiro de tudo, certifique-se de usar os cabeçalhos de kernel adequados. Presumo que os cabeçalhos e o código fonte do kernel sejam mais atualizados que o kernel que você está executando.

Tente fazer uma apt-get update && apt-get upgradereinstalação do módulo. Se o problema persistir, verifique três vezes se os cabeçalhos do kernel correspondem ao kernel atual, recompile novamente e tente instalar.


Nota: estou usando Jessie.

UPDATE: Execute-os como root.

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

# Update the kernel!
rpi-update

Pode ser necessário reiniciar. Depois disso, continue com os comandos abaixo, ainda usando a conta 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

Se rpi-sourcegerar um erro do GCC (algo sobre uma incompatibilidade de versão), tudo bem desde que sua versão atual do GCC seja superior . Executar em rpi-source --skip-gccvez derpi-source

Em seguida, continue com o seu exemplo Hello World. Crie a pasta e cdnela. Em seguida, crie os arquivos.

mkdir hello
cd hello

Arquivos:

hello.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 ( diferencia maiúsculas de minúsculas?)

obj-m := hello.o

Agora que você possui seus arquivos, pode prosseguir e executar os comandos de construção habituais do Hello World:

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

Agora você deve verificar dmesg. A última linha deve ser impressa Hello World :)destacada em vermelho.

Se sim, parabéns. Você acabou de criar e instalar um módulo do kernel.

Agora remova-o usando rmmod hello. dmesgagora deve imprimir Goodbye World!destacado em vermelho.

Fontes: 1 2 3

PNDA
fonte
Quando você diz para "verificar se os cabeçalhos do kernel correspondem ao kernel atual", como exatamente você quer dizer que devo fazer isso?
Edward
@ Edward atualizado.
PNDA
@ Edward Observe que este é o exemplo do hello world. Eu construí seu módulo, mas percebi que é o mesmo. A única diferença é que seu código não possui o destaque vermelho.
PNDA
@ Edward No seu caso, acho que seguir as instruções até que a rpi-sourcepeça seja suficiente. Você pode tentar construir o seu a partir desse ponto.
PNDA
5

Existe uma versão muito mais simples aqui, testada em jessie e stretch .

sudo apt-get install raspberrypi-kernel-headers

e quando seus arquivos estiverem no lugar:

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

Exemplo

Crie o hellodiretório, entre e crie os seguintes arquivos: hello.ce Makefile.

Eu recomendo trabalhar como usuário normal, não como root , apenas insmod, rmmode os make modules_installcomandos requerem permissões de root, e o necessário sudoé mostrado nos seguintes comandos.


hello.c (inalterado, seu arquivo)

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

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

  • Build: make(no mesmo diretório que o Makefile)
  • Teste
    • Insira o módulo com sudo insmod hello.ko
    • Encontre Hello World :)na saída dedmesg
    • Remova o módulo com sudo rmmod hello
    • Encontre Goodbye, world.int a saída dedmesg
  • Instalar, quando seu módulo estiver funcionando, sudo make modules_installinstalará o módulo onde ele pertence, assim modprobefuncionará.
pim
fonte
1
funciona muito bem para kernels instalados por meio do pacote 'raspberrypi-kernel'. Ao contrário do que a descrição emitida por 'pandalion98' se refere aos kernels instalados por meio do 'rpi-update'. Ambos os métodos são mutuamente exclusivos, certo?
Sparkie
1
Penso que esta é uma resposta válida desde OP (Edward) nunca falou sobre rpi-update, rpi-updatefoi sugerido no de pandalion98 resposta
pim
@sparkie No momento da publicação, o kernel ainda não estava integrado ao aptrepositório do Raspbian , se não me engano. Atualizar o kernel significava executar o rpi-updatescript do Hexxeh . Hoje em dia, atualizar raspberrypi-kernelou executar rpi-updatepraticamente a mesma coisa.
PNDA
Quanto a raspberrypi-kernel-headers, geralmente instala cabeçalhos de kernel incompatíveis, por experiência (os cabeçalhos tendem a ser uma versão mais nova que o kernel), por isso optei por "ir para o manual".
PNDA
parece haver alguma diferença entre 'raspberrypi-kernel' e 'rpi-update': um resulta em '4.9.66+' e o outro em '4.9.59+' neste momento. Então eu acho que ainda temos que lidar com ambos os procedimentos de compilação separadamente
sparkie
2

no getKernel.sharquivo add

sudo modprobe configs

antes

zcat /proc/config.gz >.config

(agora na imagem padrão rpi /proc/config.gz não existe)

Igor Nikolaev
fonte