Anwendungsentwicklung mit QEMU
19. Juni 2019
Ein funktionierendes Linux-Image wurde im vorherigen Beitrag erstellt. Damit wurde der Grundstock gelegt, fehlt noch die eigene Anwendung. Gängige Praxis ist es sich über eine (virtuelle) Netzwerkschnittstelle mit dem Zielsystem zu verbinden und die Applikation aufzuspielen. Dazu bieten sich zwei Möglichkeiten. NFS und SSH. Bei ersterem muss der Host als Server dienen und das Zielsystem muss sich aktiv mit dem Host verbinden und bei zweiterem muss auf dem Zielsystem ein SSH-Server laufen mit dem sich der Host verbinden kann. Da der Umgang mittels SSH wesentlich geläufiger ist, ist es die erste Wahl.
In der Standardeinstellung stellt QEMU einen DHCP-Server bereit, der selbst auf die IP-Adresse 10.0.2.2
hört und Adressen ab 10.0.2.15
verteilt.
Weiterhin ist über 10.0.2.3
noch ein virtueller DNS-Server vorhanden und unter 10.0.2.4
eine SAMBA-Freigabe.
Der dafür nötige Aufruf vereinfacht das Skript wie folgt.
#!/bin/sh
qemu-system-arm \
-M versatilepb \
-kernel QemuArmVersatile/images/zImage \
-dtb QemuArmVersatile/images/versatile-pb.dtb \
-drive file=QemuArmVersatile/images/rootfs.ext2,if=sd,format=raw \
-append "root=/dev/sda console=ttyAMA0,115200" \
-m 32 \
-serial stdio \
-name Versatile_ARM_EXT2
#!/bin/sh
qemu-system-arm \
-M vexpress-a9 \
-kernel QemuArmVexpress/images/zImage \
-dtb QemuArmVexpress/images/vexpress-v2p-ca9.dtb \
-drive file=QemuArmVexpress/images/rootfs.ext2,if=sd,format=raw \
-append "rw console=ttyAMA0 console=tty root=/dev/mmcblk0" \
-cpu cortex-a9 \
-m 32 \
-serial stdio \
-name Vexpress_ARM
Um zu überprüfen, ob das Zielsystem auf die Außenwelt zugreifen kann, kommt einem normalerweise als erstes ping
in den Kopf.
Das funktioniert allerdings nicht!
ICMP-Pakete werden nicht über TCP/IP verschickt und man benötigt deshalb root-Rechte.
Viel einfacher geht es mit der Installation des Pakets links
, einem Webbrowser für die Konsole, so gelangt man mit links google.com
ins Internet.
Fehlt noch die Gegenrichtung.
Hierbei tritt als generelle Problematik auf, dass ein Dienst verwendet werden soll, der auf Ports arbeiten möchte, die i.d.R. vom Host-System belegt sind bzw. für deren Verwendung Administratorrechte nötig wären.
Bei SSH ist das der Port 22.
Um im Zielsystem nicht unnötige Konfigurationen vornehmen zu müssen, greift man auf Portweiterleitung innerhalb QEMUs zurück.
Auf diesen Weg kann z.B. der Port 22 des Zielsystem über den Port 2222 des Hostsystems angesprochen werden.
Damit wird aus ssh root@zielsystem
, das so nicht erreichbar ist, ssh -p 2222 root@localhost
.
Später wird noch ein weiterer Port benötigt über den der gdbserver
mit seinem Client gdb
kommunizieren kann.
Standardmäßig tun sie das auf Port 2459 und da auf dem Hostsystem standardmäßig kein gdbserver
im Hintergrund läuft, können hier die gleichen Ports genutzt werden.
Das Skript muss dafür um die Weiterleitung erweitert werden.
#!/bin/sh
qemu-system-arm \
-M versatilepb \
-kernel QemuArmVersatile/images/zImage \
-dtb QemuArmVersatile/images/versatile-pb.dtb \
-drive file=QemuArmVersatile/images/rootfs.ext2,if=sd,format=raw \
-append "root=/dev/sda console=ttyAMA0,115200" \
-m 32 \
-serial stdio \
-nic user,hostfwd=tcp::2222-:22 \
-nic user,hostfwd=tcp::2459-:2459 \
-name Versatile_ARM_EXT2
#!/bin/sh
qemu-system-arm \
-M vexpress-a9 \
-kernel QemuArmVexpress/images/zImage \
-dtb QemuArmVexpress/images/vexpress-v2p-ca9.dtb \
-drive file=QemuArmVexpress/images/rootfs.ext2,if=sd,format=raw \
-append "rw console=ttyAMA0 console=tty root=/dev/mmcblk0" \
-cpu cortex-a9 \
-m 32 \
-serial stdio \
-nic user,hostfwd=tcp::2222-:22 \
-nic user,hostfwd=tcp::2459-:2459 \
-name Vexpress_ARM
Momentan kann das gebootete Image mit den Anfragen an Port 22 nicht viel anfangen, da der Dienst für diese im Image noch nicht enthalten ist und das root
Passwort nicht gesetzt ist.
Die Konfiguration muss daher nochmals angeworfen werden.
$ make O=../QemuArmVersatile menuconfig
$ make O=../QemuArmVexpress menuconfig
Für den SSH-Server wird der deutlich schlankere dropbear verwendet.
Der liegt unter Target packages ---> Networking applications ---> dropbear
und beim Aktivieren werden automatisch die client programs
mitaktiviert, die allerdings nicht benötigt werden.
Mit diesen Einstellungen könnte man zwar Verbindungsversuche mit dem SSH-Server unternehmen, allerdings wird man bei der Eingabe des Passworts scheitern.
Das muss noch gesetzt werden.
Dazu setzt man unter System configuration ---> Root password
das Passwort im Klartext oder verschlüsselt mittels mkpasswd
.
$ mkpasswd
Password:
$1$C5PwcjBt$GLW7U7xx7nNqwunEfpY680
Die Ausgabe von mkpasswd
muss hierbei noch angefasst werden, da einzeln vorkommende $-Zeichen als Variablen interpretiert werden.
Damit wird $1$C5PwcjBt$GLW7U7xx7nNqwunEfpY680
zu $$1$$C5PwcjBt$$GLW7U7xx7nNqwunEfpY680
.
Anschließend lässt man das Image neu erstellen.
$ make O=../QemuArmVersatile
$ make O=../QemuArmVexpress
Damit ist nun die erste Grundvoraussetzung gelegt.
gdbserver
Zum Debuggen braucht es den gdbserver für dessen Installation die Konfiguration nochmals angefasst werden.
$ make O=../QemuArmVersatile menuconfig
$ make O=../QemuArmVexpress menuconfig
Im Menüpunkt Toolchain
müssen folgende Einstellungen gesetzt werden.
|
|
Erst dann ist unter Target packages ---> Debugging, profiling and benchmark
gdb
mit all seinen Unterpunkten anwählbar.
|
|
Es ist gut möglich, dass das Bauen durch die neu gesetzten Einstellungen für die Toolchain
fehlschlägt.
Am schnellsten hilft das Leeren des build
Ordners.
Vorausgesetzt das erneute Bauen des Zielsystems hat funktioniert, bedarf es noch einer Applikation. Als typisches Beispiel sei ein Hallo-Welt-Programm gezeigt.
#include<stdio.h>
void main()
{
int res=0;
res+=10;
printf("Hallo %d Welt!\n",res);
}
Dieses liegt in seinem eigenen Ordner helloWorld
, der sich auf der gleichen Ebene befindet wie das QEMU-Startskript.
Der Ordner kann auch wo anders liegen, aber die Pfadangabe zum Compiler wird im Folgenden relativ angegeben.
Übersetzt wird der Code nun mit wie folgt.
$ cd helloWorld
$ ../QemuArmVersatile/host/bin/arm-buildroot-linux-uclibcgnueabi-gcc -g3 -o helloWorld helloWorld.c
$ file helloWorld
helloWorld: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, with debug_info, not stripped
$ cd helloWorld
$ ../QemuArmVexpress/host/bin/arm-buildroot-linux-uclibcgnueabihf-gcc -g3 -o helloWorld helloWorld.c
$ file helloWorld
helloWorld: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, with debug_info, not stripped
Kurz besprochen seien die Parameter -g
, der die Menge der Debuginformationen festlegt (-g0
= keine, -g1
= minimal, -g
standard, -g3
maximal), -O
zum Setzen des Optimierungslevels (-O0
keine, -Os
größenoptimiert, -O2
und -O3
geschwindigkeitsoptimiert) und -o
setzt den Dateinamen des Kompilats.
Der anschließende Aufruf von file
soll nur zeigen, dass das Kompilat auch für das Zielsystem erstellt wurde.
Jetzt muss die Datei noch ins Zielsystem übertragen werden.
Das geht mittels scp
das zur SSH gehört.
$ scp -P 2222 helloWorld root@localhost:/root
Auf dem Zielsystem kann die Anwendung so
$ ./helloWorld
Hallo 10 Welt!
gestartet werden und die erwartete Ausgabe bewundert werden.
So weit so gut, aber gedebuggt wurde bis jetzt kein Stück.
Dazu muss auf dem Zielsystem der gdbserver
mit der zu debuggenden Anwendung und dem Port als Parameter aufgerufen werden.
$ gdbserver :2459 ./helloWorld
Process ./helloWorld created; pid = 873
Listening on port 2459
Der Server lauscht nun auf Port 2459, fehlt noch der Client des Hostsystems, der sich mit ihm verbindet.
$ cd helloWorld
$ ../QemuArmVersatile/host/bin/arm-buildroot-linux-uclibcgnueabi-gdb ./helloWorld
GNU gdb (GDB) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=arm-buildroot-linux-uclibcgnueabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./helloWorld...done.
(gdb) target remote localhost:2459
Remote debugging using localhost:2459
$ cd helloWorld
$ ../QemuArmVexpress/host/bin/arm-buildroot-linux-uclibcgnueabihf-gdb ./helloWorld
GNU gdb (GDB) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=arm-buildroot-linux-uclibcgnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./helloWorld...done.
(gdb) target remote localhost:2459
Remote debugging using localhost:2459
Prinzipiell kann es jetzt losgehen! Die Benutzung der Konsole zum Debuggen ist nicht jedermanns Sache, deshalb wird ein weiterer Beitrag folgen, der das Entwickeln mit einer IDE zeigt, was das Debuggen deutlich vereinfacht.