Skip to content
On this page

8. Applikationen Cross-Compilieren

Manpages

Um die Hilfeseiten der Standard C Library einzusehen, installieren Sie das Ubuntu Package manpages-dev oder falls Sie Deutsch bevorzugen manpages-de-dev wonach per man funktionsname die Hilfeseiten zu jeder vorhandenen C-Funktion oder System Call einsehbar ist sollten.

Beispiel: Eingabe von man gethostname die Hilfeseite der C-Funktion gethostname() erscheinen. Seitenweise Blättern per Leerschlag-Taste, navigieren per Cursor-Tasten, beenden mittels q wie quit.

Sections

Die man-Pages sind in verschiedenen so genannten Sections unterteilt. Sofern es eine gleichnamige man-Page in mehreren Sections gibt ,wird die Hilfeseite der zuerst gefundenen Section angezeigt.

Beispiel: Bekanntlich existiert printf als C Standard Library Funktion. Bei man printf wird jedoch die Hilfeseite des printf Console Commands anzeigt, da es selbigen tatsächlich gibt und die Hilfeseiten der Ausführbaren Programme in einer tieferen Section als jene der Library-Funktionen sind!

Man kann bei man aber die Section auch explizit angeben, wenn man diese kennt...

Alle Bezüge in Hilfeseiten in welchen printf ganz oder teilweise vorkommt, lassen sich per apropos printf auffinden (resp. man -k printf).

Dadurch kann man erfahren, dass printf zumindest in den man-Sections 1, und 3 vorkommt.

Per man man erfahren Sie die Zuordnung der Manpage Sections. Für uns derzeit interessant:

Section
Beschreibung
Section 1 und 8
Section 2
Section 3

Die gewünschte Hilfeseite der C Library Funktion printf() erhalten Sie gem. man man unter zusätzlicher Angabe der Section Nr, also per:

man 3 printf

man Pages sind nur auf dem Linux-Host vorhanden und nicht auf unserem Buildroot Zielsystem!

Hello World Programm

Erstellen Sie unter ~/ebssd/ ein Verzeichnis workspace und darunter ein Projektverzeichnis hello, worin Sie ein C-Sourcefile hello.c mit folgender Funktionalität in der Funktion main() erstellen:

  • Einlesen des Hostnamen per gethostname() in ein char-Array
  • sowie formatierte Anzeige des Rückgabewertes von gethostname() per Funktion printf()

Includes

Tipp: Welche Include-Datei Sie für printf() includen müssen, dürfte Ihnen wohl bekannt sein – andernfalls wird es man 3 printf in einer der obersten Zeilen angezeigt! Ebenso für gethostname().

Compilieren und testen Sie Ihr Programm zuerst auf dem Hostsystem:

bash
gcc -o hello hello.c
./ hello

Identifizieren Sie das Dateiformat des generierten „Executable“ also des ausführbaren Programms: file hello.

Alles wie erwartet? (Ziel Architektur, statisch oder dynamisch gelinkt, …) Ok bestens!

Wenn Sie nun versuchen wie folgt hello.c für die ARM-Platform zu crosscompilieren, wird es vermutlich nicht klappen ( versuchen Sie's).

arm-none-linux-gnueabihf-gcc -o hello hello.c

Offensichtlich ist der betreffende Cross-Compiler obwohl von Buildroot ja heruntergeladen (oder bereits für den Custom Kernel selber heruntergeladen) nicht im "normalen" Suchpfad!

Libraries

Buildroot hat ja aus der von Buildroot heruntergeladenen Cross-Toolchain die Shared Libraries auf dem Zielsystem unter /lib und /usr/lib installiert. Shared Libraries sind dynamisch ladbare Libraries!

Um Bibliothekskonflikte mit diesen auf dem Zielsystem installierten Shard Libraries zu verhindern, sollte beim Corss-Compilieren von Anwendungen welche Shared Libraries verwenden (was standardmässig der Fall ist) der exakt gleiche Toolchain-Release verwendet werden!

Deshalb vermeiden wir es, über den Packet Manager oder anderweitig eine weitere Cross-Tolchain zu installieren und verwenden die von Buildroot installierte!

Um die durch Buildroot installierte Toolchain aufzufinden, suchen Sie selbige unter dem buildroot Ordner Z.b. wie folgt mittels find Command:

bash
# je nach dem, wo der cross-compiler abgelegt wurde
find ~/ebssd/buildroot -name 'arm*gcc'

Worauf der absolute Pfad des Crosscompiler angezeigt werden sollte! (der 2. angezeigte Pfad ist korrekt!)

Um nun nicht jedes mal den gesamten Pfad angeben müssen, ergänzen Sie den Suchpfad (also die Umgebungsvariable PATH) über das (versteckte) Login-Script .profile in Ihrem Home-Directory:

nano ~/.profile

Ergänzen Sie in dieser Datei zuunterst (nach dem fi) eine neue Zeile mit: PATH=$PATH:<vollständiger-pfad-zum-gewünschten-bin-ordner>

Pfade

Für <vollständiger-pfad-zum-gewünschten-bin-ordner> geben Sie den oben mit find angezeigten Ordnerpfad angeben, also ohne die Programmangabe am Schluss!

Die Datei .profile wird von der Login-Shell nur beim erneuten Einloggen automatisch eingelesen. Für die laufende Shell können Sie diese auch mittels dem . oder source Shell-Command includen:

bash
. ~/.profile

Wonach Sie sich vergewissern, ob die Tools nun wirklich gefunden werden

bash
arm<TAB><TAB>
# also arm eintippen und dann 2x die Tabulatortaste betätigen

Damit sollten alle Cross-Compiler-Tools inkl. C-Compiler arm-none-linux-gnueabihf-gcc angezeigt werden.

Command nicht gefunden

Fehlersuche: Falls arm-none-linux-gnueabihf-gcc nicht gefunden wird, funktionierte das Setzen des Pfads offensichtlich nicht!

Ein echo $PATH muss am Ende auch den Pfad zur Toolchain nur bis zum bin Ordner anzeigen.

Beachten Sie, den Command-Präfix 'arm-none-linux-gnueabihf-' welcher folgende Bedeutung hat:

armARM v7 32-bit Architektur entsprechend der CPU-Architektur des SoCs . Diese würde übrigens auch ARM64-Bit verstehen – vorausgesetzt ein 64-Bit Linux-Kernel würde gebootet und ARM64-Bit Shared Libraries würden installiert!)
linuxmit Linux Systemcalls sowie Virtual Memory Unterstützung (also nicht "bare metal" d.h. ohne VM)
gnueabihfmit GNU C/C++ Library also gnu→mit glibc, eabi → Embedded Application Binary Interface (spezifiziert Aufrufkonvention) sowie hf → mit Hardware-Floatingpoint Unterstützung.

Welchen GCC Release hat der Compiler selbst? (Compiler aufrufen mit Option --version)

Sofern alles klappte, melden Sie sich am Linux-System ab und neu an, damit die Toolchain in allen neuen Shells automatisch verfügbar wird (Umgebungsvariablen sind ja nicht „global“ sonder bloss „vererbt“).

Das Cross-Compilieren sollte nun erfolgreich sein:

bash
arm-none-linux-gnueabihf-gcc -o hello hello.c

Identifizieren Sie das generierte „Executable“ wiederum per:

bash
file hello

Alles wie erwartet? (Korrekte Ziel-Architektur? Statisch oder Dyamisch gelinkt?, …) Ok bestens!

Der Einfachheit halber kopieren wir das gerade erstellte hello Programm aufs Zielsystem nach /usr/bin/ Achtung: im betreffenden Ordner sind auch von Buildroot generierte ELF-Dateien. Vermeiden Sie also Konflikte mit den dort vorhandenen Dateien resp. Dateinamen!

Kopieren Sie das gerade erstellte hello Programm über die (funktionierende!) Netzwerkverbindung zum laufenden Zielsystem mittels scp in das Zielsystem-Verzeichnis /usr/bin/

Binary Location

Alternativ könnten Sie das hello „Binary“ auch ins Homeverzeichnis des root-Users (/root auf dem Zielsystem) oder besser noch ins Zielsystem-Verzeichnis /usr/local/bin kopieren, müssten dann aber die Umgebungsvariable PATH im systemweiten /etc/profile ergänzt oder bei jedem Aufruf den gesamten Pfad angeben werden!

Hat‘s funktioniert? Andernfalls korrigieren Sie den Fehler! (Insbes. falls /usr/bin/ auf dem Target noch gar nicht existiert oder nicht im Suchpfad enthalten wäre...)

  • Welche Art Libraries werden gem. file hello (auf Hostssytem ausgeführt) verwendet?

  • Und wie gross ist das Programm gemäss ls -l hello überhaupt?

Standard C Lib

Die Ausführung des Commands funktioniert also bloss, weil die GNU Standard C Library (kurz glibc) als „Shared Libraries“ auf dem Zielsystem korrekt unter /lib... und /usr/lib/... installiert ist sowie beim Crosscompileieren von hello.c die exakt gleiche Library-Version referenziert wurde!

(Buildroot hat diese Shared Libraries ja aus dem "sysroot" der Cross-Toolchain in die tar-Datei kopiert...)

  • Übersetzen und linken Sie nochmals unter zusätzlicher Angabe der Option -static und verifizieren Sie danach per file hello ob das Programm nun wirklich statisch gelinkt wurde. (Es sollte nun!)

  • Wie gross resp. wie viel mal grösser ist nun das Programm?

Mittels arm-none-linux-gnueabihf-strip hello könnten Sie noch alle (Debug-) Symbol-Information aus dem Programm entfernen, das „Binary“ bleibt aber trotzdem noch recht gross für einen „Einzeiler“!

Statisches Linken

Selbst ein kleines „Hello World“-Programm ist also mit statisch eingebundener GNU Standard C Library recht gross!

Das „vollgepackte“ dynamisch gelinkte Programm /bin/busybox ist sogar noch kleiner!

Für einzelne Programme wäre dies kein Problem,aber bei einer grossen Anzahl Programme ist es doch sinnvoller die Libraries dynamisch einzubinden (d.h. als so genannte „Shared Libraries“).

Statisches linken verlangsamt auch die Programmstartzeit und verschwendet insbes. bei einer grösseren Anzahl Programmen viel Flash-Speicher (SD-Karte) und Hauptspeicher (DRAM), wogegen Shared Libraries wie der Name sagt, unter den laufenden Programmen „geshared“ also gemeinsam genutzt werden!

Mit Buildroot wäre es auch möglich, eine Toolchain mit der ucLibc Library generieren zu lassen, was sich jedoch nur bei sehr knappem Systemspeicher lohnt (also bei sehr wenig Flash und/oder DRAM)! (vgl. http://www.uclibc.org/FAQ.html#why_should_i)

Da wir auf unserem Zielsystem reichlich Speicherplatz haben (sowohl auf der SD wie im RAM), bleiben wir bei der durch Buildroot installierten GNU Toolchain beinhaltend die GNU Standard C Library (glibc)

Shared Libraries

Diese Toolchain enthält also die GNU C Standard Library und zwar gleich doppelt, einerseits statisch linkbar (*.a) als auch als Shared-Libraries (*.so steht für „Shared Object“). Beide Library-Varianten befinden sich unter dem so genannten sysroot Verzeichnis der Toolschain.

Das sysroot des Hostsystems ist zuoberst im Root-Verzeichnis auf /, wogegen sich das sysroot der Crosstoolchain wie folgt anzeigen lässt: arm-none-linux-gnueabihf-gcc --print-sysroot

Wenn Sie nun unter dem Toolchain-sysroot die Grösse der Zielsystem-Verzeichnisse /lib und /usr/lib bestimmen (z.B. auf dem Zielsystem mittels du -sh /lib /usr/lib) und mit jenen auf dem Hostsystem vergleichen – also nach cd ins betreffende sysroot-Verzeichnis mit du -sh lib usr/lib , erkennen Sie, dass Buildroot offensichtlich bloss ein Bruchteil aller vorhandenen Shared Libraries kopiert hat!

fehlende libs

Beim manuellen kompilieren und zufügen von Anwendungen zu einer Buildroot Installation kann es somit passieren, dass die eine oder andere Shared-Libraries fehlt oder fehlen, d.h. nicht auf dem Zielsystem installiert sind.

Gegebenenfalls müssten Sie die betreffenden Shared-Libraries manuell aus dem sysroot der Toolchain auf das Zielsystem resp. deren SD kopieren kopieren (per cp ... resp. scp ... ).

Aber wie erkennt man, welche „Shared Libraries“ von einem Programm benötigt werden?

Hierzu gibt es auf dem Hostsystem der ldd-Befehl, welcher die von einem Programm benötigten Shared-Libraries auflistet, beispielsweise betreffend der bash Shell mit: ldd /bin/bash.

ldd auf dem Host funktioniert jedoch nicht für crosscompilierte Programme und nicht immer ist ldd auf einem Embedded Zielsystem vorhanden.

In diesem Fall kann statt dessen der so genannte „Library Loader“ (also jene „Shared Library“, welche alle anderen Shared Libraries nachzuladen vermag) selbst als Programm mit Option --list aufgerufen werden, auf dem Zielsystem also mit:

bash
/lib/ld-linux-armhf.so.3 --list /usr/bin/hello
  • Welche Shared Libraries werden aufgelistet und wozu werden diese in hello wohl verwendet?

Wenn Sie obige Command mehrfach aufrufen erkennen Sie, dass die Loader-Adressen der Shared Libraries jedesmal ändert. Aus Sicherheitsgründen resp. um Hacker-Angriffe zu erschweren führt Linux beim Laden der Programme eine Address Space Layout Randomization (ASLR) durch, vgl. https://de.wikipedia.org/wiki/Address_Space_Layout_Randomization.

Libraries und Memory Map

Damit einerseits der GNU Library Loader die von einer Applikation gewünschten Libraries schneller auffinden kann, andererseits dieser auch Libraries ausserhalb der Standard-Verzeichnisse /lib und /usr/lib finden kann, ist zumindest auf dem Hostsystem ein Library-Index Cache-Datei /etc/ld.so.cache vorhanden.

Falls Buildroot Programme installiert hätte, welche abgesehen von der Standard C Library weitere Shared Libraries benötigen, hätte Buildroot diese Index-Datei für uns ebenfalls erstellt.

Zudem liessen sich weitere Library-Verzeichnisse in /etc/ld.so.conf eintragen, wonach mittels ldconfig der Library-Cache neu erstellt werden müsste. Leider hat Buildroot diesen Command nicht aus dem Toolchain sysroot auf die Zielsystem-Partition kopiert, da es gegen die Philosophie von Buildroot spricht, nachträglich das Image zu ändern. (Man könnte buildroot aber entsprechend konfigurieren oder ldconfig manuell aus dem "sysroot" auf Zielsystem kopieren).

Die „Memory Map“ laufender Programme (inkl. eingebundene Shared Libraries) lässt sich hingegen über das Pseudofilesystem /proc per cat /proc/<pid>/maps ermitteln (an Stelle von <pid> die Prozess-Id des gewünschten Prozesses angeben).

Betrachten Sie auf dem Host- wie auf dem Zielsystem auch noch die Memory Map des init-Prozesses.

Der init Prozess hat bekanntlich immer die PID= , da dieser ja als erster Prozess gestartet wurde.

Verifizieren Sie dies per ps resp. ps -ef.

Makefile für Hello-Programm

Um den Aufruf des Buildvorganges zu vereinfachen, sodass der Build-Vorgang mittels einem einfachen make erfolgen kann, erstellen Sie im Projektordner hello folgende Datei namens Makefile.

Makefile
CC = arm-none-linux-gnueabihf-gcc
CFLAGS = -mthumb -march=armv7-a
CFLAGS += -Wall

all: hello

hello: hello.c
    $(CC) $(CFLAGS) -o hello hello.c #unbedingt ein TAB am Anfang dieser Zeile verwenden!!!

Whitespace

Achtung: der Whitespace vor $(CC) … muss zwingend ein <Tab> Zeichen sein, keine Leerschläge!

Bemerkung zu den angegebenen CFLAGS:

  • Mit Angabe der Compiler-Optionen -mthumb und -march=armv7-a wird statt dem defaultmässigen 32-Bit ARM Maschinencode der kompaktere (gemischt 16/32-Bit) „Thumb2“ Maschinencode generiert.
  • ... unsere Ziel-CPU (auf dem SoC) versteht jedoch beides, d.h. diese CFLAGs könnten ohne weiteres auch weggelassen werden.

CFLAGS

Sofern das Ausgabefile hello schon existiert und dieses neuer als der abhängige Sourcecode hello.c ist, wird make bekanntlich die Commands in der Regel hello: hello.c nicht anwenden und deshalb hello.c nicht neu übersetzt!

Somit erfolgt z.B. nach Änderungen am Makefile (z.B. ändern der CFLAGS-Optionen) keine Neukompilation beim Ausführen von make (s.a. nächstes Kapitel).

Übrigens: die make-Rule hello: hello.c müssten wir eigentlich gar nicht aufführen, denn sie wird durch eine „Built-In Rule“ von make bereits abgedeckt. Um jedoch vom make-Release unabhängig zu sein und der besseren Dokumentation wegen, sollten derartige make built-In-Rules besser gar nicht verwendet werden!

Makefile vervollständigen

Testen Sie das Makefile per make aus dem hello-Projektverzeichnis, wobei Sie vorgängig das hello-Executable löschen (nicht den Sourcecode!).

Um ein Neuerstellen von 'hello' zu erzwingen, könnten Sie selbiges jeweils manuell löschen und danach erneut make ausführen. Üblicherweise erstellt man hierzu eigens eine Makefile-Rule namens clean, mit welcher per Aufruf von make clean das Executable (hello) sowie etwelche weitere Objektfiles gelöscht werden.

Achtung

Die erste Rule im Makefile ist die Default-Rule, weshalb bei uns die Rule 'all' zuoberst sein muss resp. bleiben soll!

  • Erstellen Sie auch eine Rule clean: mit welcher Sie das hello-Executable löschen per: rm -f hello

Da clean keine effektiv zu erstellende Ziel-Datei ist, sondern bloss ein „scheinbares“ (phony) Ziel, ergänzen Sie zuunterst im Makefile noch die Zeile:

.PHONY: clean

(Die Rule 'all' ist übrigens defaultmässig eine "phony rule")

Erstellen Sie zudem noch eine Rule install:, mit welcher das hello-Programm mittels scp auf's Zielsystem unter /usr/bin/ kopiert wird.

SSH Keys

Spätestens wenn ein System an einem LAN oder gar per Port-Forwarding am Internet angeschlossen wird, sollte ein sicheres Passwort auf dem Ziel- resp. Remotesystem verwendet werden.

Statt dieses bei jedem Remote-Login per ssh oder scp manuell anzugeben, verwendet man besser und bequemer eine asymetrische „SSH Key-Based Authentication“. Hierzu ist einmalig auf dem Hostsystem ein RSA-Schlüsselpaar per ssh-keygen -t rsa -b 2048 zu generieren, danach kann der generierte Public Key per ssh-copy-id root@192.168.x.y auf das Zielsystem kopiert werden. Der lokale Private Key unter dem ~/.ssh/ wird hingegen „geheim“ gehalten!

Sofern das Zielsystem später mal gewechselt oder neu aufgesetzt wird, reklamiert ssh/scp aus Sicherheitsgründen. Um das Problem zu korrigieren muss der "Fingerprint" des Remote Hosts aus der lokalen ~/.ssh/known_hosts Liste entfernt werden – bequem mittels ssh-keygen -R 192.168.x.y.

  • Generieren Sie wie oben beschreiben ein RSA-Schlüsselpaar und kopieren Sie den Public Key wie angegeben aufs Zielsystem.

  • Nun sollte ein make clean && make && make install oder kürzer ein make clean all install klappen!

Wenn alles klappt, suchen/ersetzen Sie im Makefile noch alle Vorkommnisse von hello mit $(APP) und definieren zuoberst eine make-Variable APP=hello, womit das Makefile für zukünftige Anwendungen einfach anpassbar wird!

Remote-Debugging mit GDB auf Console-Ebene

Ein rudimentäres GDB Cross-Debuggen ist aber auch mittels gdb möglich, und via gdb-Option -tui sogar mit einem „Trivial User Interface“ .

  • Der tui Mode von GDB erfordert jedoch die Packages: libncursesw5 libpython2.7: sudo apt install libncursesw5 libpython2.7
  • Ergänzen Sie im Makefile in einer der CFLAGS Zeilen noch -g, damit der Compiler dem Executable die Debug-Informationen zufügt: CFLAGS += -Wall -g
  • Starten Sie nach Neuerstellung von hello selbiges wie folgt auf dem Zielsystem unter der Kontrolle des GNU Debug Servers 'gdbserver':
bash
gdbserver :2000 /usr/bin/hello

Das Programm gdbserver lädt das angegebene Programm (/usr/bin/hello) in den Speicher und empfängt auf dem angegebenen und hoffentlich freien TCP-Port (2000) auf allen lokalen IP-Interfaces auf eine eingehende Verbindung und in der Folge auf Steuercommands des entfernten gdb-Debugger-Frontends. (Sofern Sie in der Buildroot menuconfig Unter Toolchain den dbserver ausgewühlt haben, wurde dieses aus dem „sysroot“ der Toolchain auf das Zielsystem Image unter /usr/bin/ kopiert!)

Starten Sie aus dem hello-Projektverzeichnis auf dem Hostssytem nun den GDB Cross-Debugger der Toolchain mit dem „Terminal User Interface“ (-tui) per:

arm-none-linux-gnueabihf-gdb -tui hello

Wonach Sie in der GDB-Console folgendes eingeben, um die Remote-Verbindung zum gdbserver herzustellen und ein paar Schritte zu Debuggen:

(gdb) target remote 192.168.254.254:2000 # stellt Remote-Verbindung zum gdbserver her
(gdb) br main # setzt Breakpoint auf Symbol 'main'
(gdb) cont # führt Programm bis zum Breakpoint ('main') aus
(gdb) next # führt bis zur nächsten Sourcezeile aus (inkl. Unterprogramme)
(gdb) step # steppt auch in Unterprogramme (ohne setzen des sysroot ev. crash)
(gdb) finish # step out (Ausführen der aktive Funktion bis und mit Rücksprung)
(gdb) print <variablenname> # um eine Variable zu inspizieren
(gdb) quit # aus gdb aussteigen

GDB Quick References und Anleitungen finden Sie auf:

Je nach gcc- resp. gdb-Version erfolgt eventuell ein Crash beim Steppen in C Standard Library Funktionen, sofern das GDB-Frontend (arm-none-linux-gnueabihf-gdb) die falschen Shared-Libraries annimmt (nämlich jene des Hostsystems mit x86-Maschinencode). Gegebenenfalls lässt sich dieses Problem lösen, indem entweder die Anwendung hello statisch gelinkt wird (was bekanntlich per Compilerflag -static möglich ist) oder indem dem GDB-Frontend in der GDB-Console der Pfad zum richtigen sysroot bekanntgegeben wird per:

(gdb) set sysroot <sysroot-path>

Den <sysroot-path> (also den Pfad zu den Shared Libraries der Cross-Toolchain) erfahren Sie per:

  • arm-none-linux-gnueabihf-gcc --print-sysroot

Ein vorzeitiges Abbrechen von hello mittels Ctrl-C auf dem Zielsystem ist bei angehaltenem Debugger übrigens nicht möglich, da der Prozess hello ja unter der Kontrolle des gdbserver Prozesses angehalten ist und gdbserver selbst wiederum den Standard-Input dem (angehaltenen) Prozess hello überlässt.

Um beide Prozesse zu beenden müssen Sie sich schon auf dem Zielsystem unter einer weiteren Shell-Console (z.B. via ssh) einloggen und danach z.B. mittels killall gdbserver hello beide Prozesse beenden (oder „brute force“ das Zielsystem rebooten...)

Damit Sie das set sysroot … nicht bei jeder Debugsession manuell neu eingeben müssen, definieren Sie den obigen gdb-Command am Besten im Projektverzeichnis in die (versteckte) und von gdb automatisch eingelesene Datei .gdbinit was Sie wie folgt realisieren können:

bash
echo "set sysroot `arm-none-linux-gnueabihf-gcc --print-sysroot`" > .gdbinit

Neuere GDB-Releases lassen aus Sicherheitsgründen keine .gdbinit-Dateien aus beliebigen Ordnern zu. Dies kann aber in einer .gdbinit-Datei im Homeverzeichnis mittels eines Eintrages set auto-load safe-path / zugelassen werden. Führen Sie deshalb auch noch folgendes aus:

bash
echo 'set auto-load safe-path /' > ~/.gdbinit

Der Bequemlichkeit halber können Sie in der Projekt .gdbinit Datei auch folgende Zeilen ergänzen:

target remote 192.168.254.254:2000 # stellt Remote-Verbindung zum gdbserver her
br main # setzt Breakpoint auf Symbol 'main'
cont # führt das Programm bis zum Beakpoint ('main') aus

Wenn bisher alles geklappt hat und Sie (fakultativ) noch eine weitere Herausforderung suchen, können Sie im Makefile zusätzlich eine Phony-Rule namens gdb ergänzen, in welcher dann zuerst der gdbserver auf dem Zielsystem gestartet und danach der Cross gdb auf dem Host gestartet wird.

Für komplexere Applikationen lohnt sich die Evaluierung einer Featurereichen IDE wie z.B. Eclipse, VS-Code, Geany oder CLion. Häufig lohnt es sich aber, sich nicht vollständig von der IDE abhängig zu machen und auch weiter das Arbeiten mit einem Editor und make zu unterstützen.