Jak skompilować ładowalny moduł jądra bez ponownej kompilacji jądra

20

Przeczytałem sporo o tym, jak skompilować moduł jądra na (i dla) Raspberry Pi, ale wciąż nie jestem w stanie zrozumieć, dlaczego to nie działa. Byłem w stanie zbudować moduł, ale raportuje, Invalid module formatgdy próbuję uzyskać insmodwynik. Oto proces, który zastosowałem. Najpierw jako root w ramach /rootI wykonałem następujący skrypt powłoki:

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 

Pierwsze kilka wierszy pochodzi z http://lostindetails.com/blog/post/Compiling-a-kernel-module-for-the-raspberry-pi-2

Resztę napisałem, aby zautomatyzować większą część procesu. Gdy wszystko zakończy się powodzeniem, mam źródło, które powinno dokładnie pasować do działającego jądra, konfiguracji do dopasowania i dowiązania symbolicznego. Były pewne przekierowania z lokalizacji internetowej github (najwyraźniej jest to teraz https://raw.githubusercontent.com/ ), ale nie ma rzeczywistych błędów.

Następnie zostaję domyślnym piużytkownikiem i w katalogu o nazwie /home/pi/projects/lkmmam ten kod źródłowy dla bardzo prostego modułu zabawki:

cześć, 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);

Na koniec buduję moduł z tym 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

Wreszcie próbuję załadować moduł:

sudo insmod hello.ko

Wynik jest jednak rozczarowujący:

insmod: ERROR: nie można wstawić modułu hello.ko: nieprawidłowy format modułu

Ewentualnie istotne szczegóły

Korzystam z najnowszej jessiewersji Raspbian na 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) 

Niestety nie jestem pewien, jak rozwiązać ten problem lub go naprawić. Jakieś wskazówki?

Edward
źródło
Skompilowałem wszystkie swoje odkrycia i doświadczenia w skrypcie, patrz github.com/x29a/kernel/blob/master/rpi/prepare.sh i powiązany blog blog.chris007.de/…
x29a

Odpowiedzi:

23

Przede wszystkim upewnij się, że używasz odpowiednich nagłówków jądra. Zakładam, że nagłówki jądra i kod źródłowy są bardziej aktualne niż jądro, z którego korzystasz.

Spróbuj wykonać, a apt-get update && apt-get upgradenastępnie ponownie zainstaluj moduł. Jeśli problem nadal występuje, potrójnie sprawdź, czy nagłówki jądra są zgodne z bieżącym jądrem, ponownie skompiluj ponownie, a następnie spróbuj zainstalować.


Uwaga: używam Jessie.

AKTUALIZACJA: Uruchom je jako root.

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

# Update the kernel!
rpi-update

Może być konieczne ponowne uruchomienie komputera. Następnie wykonaj poniższe polecenia, nadal korzystając z konta 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

Jeśli rpi-sourcezgłasza błąd GCC (coś o niedopasowaniu wersji), jest w porządku, o ile aktualna wersja GCC jest wyższa . Uruchom rpi-source --skip-gcczamiastrpi-source

Następnie przejdź do przykładu Hello World. Utwórz folder i cddo niego. Następnie utwórz pliki.

mkdir hello
cd hello

Pliki:

cześć, 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 ( wielkość liter ma znaczenie?)

obj-m := hello.o

Teraz, gdy masz już swoje pliki, możesz śmiało uruchamiać zwykłe polecenia kompilacji Hello World:

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

Powinieneś teraz sprawdzić dmesg. Ostatni wiersz powinien być Hello World :)podświetlony na czerwono.

Jeśli to zrobisz, gratulacje. Właśnie stworzyłeś i zainstalowałeś moduł jądra.

Teraz usuń go za pomocą rmmod hello. dmesgpowinien teraz drukować Goodbye World!podświetlony na czerwono.

Źródła: 1 2 3

PNDA
źródło
Kiedy mówisz, aby „sprawdzić, czy nagłówki jądra pasują do bieżącego jądra”, jak dokładnie masz na myśli, że powinienem to zrobić?
Edward
@ Edward zaktualizowane.
PNDA
@Edward Zwróć uwagę, że jest to przykład hello world. Zbudowałem twój moduł, ale zdałem sobie sprawę, że jest tak samo. Jedyną różnicą jest to, że Twój kod nie ma czerwonego wyróżnienia.
PNDA
@ Edward W twoim przypadku myślę, że postępujesz zgodnie z instrukcjami, aż ta rpi-sourceczęść będzie wystarczająca. Od tego momentu możesz spróbować zbudować swój.
PNDA
5

Jest tutaj o wiele prostsza wersja, przetestowana na jessie i stretch .

sudo apt-get install raspberrypi-kernel-headers

a następnie, gdy pliki są na miejscu:

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

Przykład

Utwórz hellokatalog, wejdź do środka i utwórz następujące pliki: hello.ci Makefile.

I zalecamy pracę jako zwykły użytkownik, a nie głównym , tylko insmod, rmmodi make modules_installpolecenia wymagają uprawnień administratora, a konieczne sudojest pokazany w poniższych poleceń.


hello.c (niezmieniony, twój plik)

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

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)   

Stosowanie

  • Kompilacja: make(w tym samym katalogu co plik Makefile)
  • Test
    • Włóż moduł za pomocą sudo insmod hello.ko
    • Znajdź Hello World :)w wynikachdmesg
    • Wyjmij moduł za pomocą sudo rmmod hello
    • Znajdź Goodbye, world.int wynikdmesg
  • Zainstaluj, gdy moduł działa, sudo make modules_installzainstaluje moduł tam, gdzie należy, więc modprobebędzie działać.
pim
źródło
1
działa bardzo dobrze w przypadku jądra zainstalowanego za pomocą pakietu „raspberrypi-kernel”. W przeciwieństwie do tego opis wydany przez „pandalion98” odnosi się do jąder zainstalowanych za pomocą „aktualizacji rpi”. Obie metody wykluczają się wzajemnie, prawda?
sparkie
1
Myślę, że jest to poprawna odpowiedź, ponieważ OP (Edward) nigdy nie rozmawiał rpi-update, rpi-updatezasugerowano w odpowiedzi pandalion98
pim
@sparkie W momencie wysyłania jądro nadal nie było zintegrowane z aptrepozytorium Raspbian , jeśli się nie mylę. Aktualizacja jądra oznaczała uruchomienie rpi-updateskryptu Hexxeh . Obecnie aktualizowanie raspberrypi-kernellub uruchamianie rpi-updaterobi prawie to samo.
PNDA,
Jeśli chodzi o raspberrypi-kernel-headersto, zwykle instaluje niedopasowane nagłówki jądra, z doświadczenia (nagłówki są zwykle nowszą wersją niż jądro), dlatego zdecydowałem się na „przejście ręcznie”.
PNDA,
wydaje się, że istnieje pewna różnica między „jądrem raspberrypi” a „aktualizacją rpi”: jedno powoduje „4.9.66+”, drugie „4.9.59+” w tym momencie. Sądzę więc, że nadal musimy osobno obsługiwać obie procedury kompilacji
sparkie
2

w getKernel.shpliku dodaj

sudo modprobe configs

przed

zcat /proc/config.gz >.config

(teraz domyślnie obraz rpi /proc/config.gz nie istnieje)

Igor Nikołajew
źródło