Ich habe irgendwie noch initiale Verständnisprobleme in der Benutzung
von GDB und Debuggen von STM32-Programmen.
Ich wollte durch das geflashte Programm steppen.
Der Vector-Bereich sieht so aus:
1
(gdb) x/20x 0
2
0x0: 0x20000330 0x00003a57 0x00003897 0x00003897
3
0x10: 0x00003897 0x00003897 0x00003897 0x00000000
Ich vermute, die Reset-Einsprungadresse ist ungerade, um den
Thumb-Instruktionssatz einzuschalten (oder wie wäre die richtige
Formulierung?)
Jetzt würde ich gerne einen Breakpoint im Reset-Code setzen:
1
(gdb) b *0x3a56
2
Breakpoint 1 at 0x3a56
Aber gdb hält dort nicht an. Was mache ich falsch? Das Programm loopte
an einer anderen Stelle herum (wie schon mal in einem frühren Thread
beschrieben). Also unterbrach ich gdb mit ^C.
1
Program received signal SIGINT, Interrupt.
2
0x0000153a in serial_emit ()
3
(gdb) quit
4
A debugging session is active.
5
6
Inferior 1 [Remote target] will be detached.
7
8
Quit anyway? (y or n) y
9
Detaching from program: /Users/kuku/Downloads/mecrisp-stellaris-2.5.4a/stm32f407/firmware.elf, Remote target
10
Ending remote debugging.
Starte ich jetzt gdb wieder, bekomme ich:
1
GNU gdb (GNU Tools for Arm Embedded Processors 8-2018-q4-major) 8.2.50.20181213-git
2
Copyright (C) 2018 Free Software Foundation, Inc.
3
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
4
This is free software: you are free to change and redistribute it.
5
There is NO WARRANTY, to the extent permitted by law.
6
Type "show copying" and "show warranty" for details.
7
This GDB was configured as "--host=x86_64-apple-darwin10 --target=arm-none-eabi".
8
Type "show configuration" for configuration details.
9
For bug reporting instructions, please see:
10
<http://www.gnu.org/software/gdb/bugs/>.
11
Find the GDB manual and other documentation resources online at:
12
<http://www.gnu.org/software/gdb/documentation/>.
13
14
For help, type "help".
15
Type "apropos word" to search for commands related to "word".
16
0x0000153a in serial_emit ()
17
(gdb)
Wieso merkt sich gdb die Stelle der letzten Unterbrechung? Ich habe den
Eindruck, daß gdb dort wieder weitermacht, wenn ich es neu starte und
c(ontinue) eingebe.
Ich krieg's einfach nicht hin, durch mein Programm zu steppen.
Gebe ich mal s ein, so steppt das Programm gleich mehrfach:
Es folgt noch mal ein Mitschnitt meiner verzweifelten Versuche:
1
GNU gdb (GNU Tools for Arm Embedded Processors 8-2018-q4-major) 8.2.50.20181213-git
2
Copyright (C) 2018 Free Software Foundation, Inc.
3
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
4
This is free software: you are free to change and redistribute it.
5
There is NO WARRANTY, to the extent permitted by law.
6
Type "show copying" and "show warranty" for details.
7
This GDB was configured as "--host=x86_64-apple-darwin10 --target=arm-none-eabi".
8
Type "show configuration" for configuration details.
9
For bug reporting instructions, please see:
10
<http://www.gnu.org/software/gdb/bugs/>.
11
Find the GDB manual and other documentation resources online at:
12
<http://www.gnu.org/software/gdb/documentation/>.
13
14
For help, type "help".
15
Type "apropos word" to search for commands related to "word".
16
0x0000153a in serial_emit ()
17
(gdb) b *0x08003a56
18
Breakpoint 1 at 0x8003a56
19
(gdb) c
20
Continuing.
21
Note: automatically using hardware breakpoints for read-only addresses.
22
23
^C
24
Program received signal SIGINT, Interrupt.
25
0x0000158c in serial_qemit ()
26
(gdb) s
27
Single stepping until exit from function serial_qemit,
28
which has no line number information.
29
halted: PC: 0x0000158e
30
halted: PC: 0x00001590
31
halted: PC: 0x00001592
32
halted: PC: 0x00001594
33
halted: PC: 0x00001598
34
halted: PC: 0x00001536
35
0x00001536 in serial_emit ()
36
(gdb) s
37
Single stepping until exit from function serial_emit,
38
which has no line number information.
39
halted: PC: 0x00001538
40
halted: PC: 0x0000153a
41
halted: PC: 0x00001532
42
halted: PC: 0x00001580
43
44
^C
45
Program received signal SIGINT, Interrupt.
46
0x00001598 in serial_qemit ()
47
(gdb)
(Anm.: daß ich jetzt versucht hatte, auf 0x08003a56 einen BP zu setzen,
war ein zusätzlicher Versuch. Ursprünglich hatte ich versucht, den BP
auf 0x3a56 bzw. 0x3a57 zu setzen.)
Was hast du denn als Anbindung an deinen STM32?
Ich vermisse da irgendwie ein "target extended-remote ..."-Kommando, mit
dem dann über den STlink (oder was auch immer du benutzt) auf einen
GDB-Server zugegriffen werden kann.
Du willst ja schließlich das Programm auf dem Controller debuggen,
nichts, was irgendwie direkt auf deinem Host läuft.
ps: Wenn du ein ELF-File mit Debuginformationen hast, ist es allemal
einfacher, symbolisch zu arbeiten, also "b main", damit nur der
Startupcode bis zum Anfang von main() abgearbeitet wird.
Aber "target extended-remote :<portnummer>" brauchst du schon irgendwie.
Möglicherweise steckt das aber in deiner .gdbinit?
Jörg W. schrieb:> Was hast du denn als Anbindung an deinen STM32?>> Ich vermisse da irgendwie ein "target extended-remote ..."-Kommando, mit> dem dann über den STlink (oder was auch immer du benutzt) auf einen> GDB-Server zugegriffen werden kann.>> Du willst ja schließlich das Programm auf dem Controller debuggen,> nichts, was irgendwie direkt auf deinem Host läuft.>> ps: Wenn du ein ELF-File mit Debuginformationen hast, ist es allemal> einfacher, symbolisch zu arbeiten, also "b main", damit nur der> Startupcode bis zum Anfang von main() abgearbeitet wird.>> Aber "target extended-remote :<portnummer>" brauchst du schon irgendwie.> Möglicherweise steckt das aber in deiner .gdbinit?
Hallo Jörg,
ich habe in ~/.gdbinit:
file firmware.elf
target remote localhost:4242
load firmware.elf
(hatte aber eben die Zeile "load firmware.elf" auskommentiert)
Und openOCD läuft. Kein C sondern Assembler. :) ABer trotzdem danke für
den Hinweis. Auch der Assembler-Code hat natürlich Symbole und
b Reset funktioniert natürlich bequemer:
OK, das sieht man leider in GDBs Startmeldungen nicht.
GDB "merkt" sich übrigens in diesem Falle nichts, sondern wenn du das
anhälst, dann ist es einfach die Information aus dem Controller selbst,
die er dir da anzeigt.
Vor dem "load" mache ich grundsätzlich ein "monitor reset halt", damit
die CPU erstmal auf Anfang steht. Gestartet wird anschließend (im
Gegensatz zum Host-GDB) nicht mit "r(un)", sondern mit "c(ontinue)".
Ich würde dir empfehlen, diese Kommandos erstmal nicht per .gdbinit
auszuführen, sondern manuell, bis alles soweit funktioniert, wie du das
willst. Das kannst du dann auch beliebig oft bei laufendem GDB neu
ausführen, also "mon reset halt", "load", "continue". Du kannst auch
zwischendrin ein "detach" machen, dann den OpenOCD anhalten, neu
starten, was auch immer, danach ein neues "target extended-remote
:4242". ("extended-remote" scheint gegenüber "remote" mittlerweile zu
bevorzugen zu sein, sagte mir jedenfalls mein GDB immer mal wieder.)
Persönliche Feststellung von mir (weiß nicht, ob ich da was falsch
mache): eine geänderte ELF-Datei (extern neu compiliert) wird nicht
sauber neu geflasht, wenn ich den GDB nicht zwischendrin mit "file"
(ohne Argumente) dazu bringe, die alte Datei komplett zu vergessen, um
sie danach mit "file firmware.elf" neu bekannt zu machen. Habe
jedenfalls schon ein paarmal bemerkt, dass ansonsten im Flash noch das
alte Image herum dümpelte.
Ein wichtiger Unterschied zwischen der Steuerung über GDB und dem
normalen Bootprozess ist übrigens, dass der GDB nach dem Reset beim
Entrypoint aus dem ELF-File startet. Den sollte man also explizit
setzen, entweder über den Linkerscript oder über -Wl,--entry=. Wenn der
Controller ohne GDB bootet, nimmt er dagegen die Adresse aus dem zweiten
Slot der Vektortabelle.
Christoph K. schrieb:> Und openOCD läuft. Kein C sondern Assembler. :)
Uff, wer macht denn sowas? :)
Ich kann den Assemblercode vom Cortex-M zwar ganz brauchbar lesen, aber
das selbst zu schreiben grenzt an Masochismus. ;-)
Jörg W. schrieb:> Christoph K. schrieb:>>> Und openOCD läuft. Kein C sondern Assembler. :)>> Uff, wer macht denn sowas? :)>> Ich kann den Assemblercode vom Cortex-M zwar ganz brauchbar lesen, aber> das selbst zu schreiben grenzt an Masochismus. ;-)
Ja, das stimmt :) Aber ich habe ein Projekt übernommen, in dem ein
Freund (†) eine Meßgerätesteuerung komplett in Forth von Grund auf
hochgezogen hat. Das System beinhaltet multi-threading und
multi-tasking. Ehe ich mich an das Projekt heranmache, übe ich etwas mit
mecrisp-stellaris-forth. Bin ja selbst mit Forth aufgewachsen. Hatte
1982 ein Forth für den 68000 entwickelt (portiert, fig-Forth 83) und
eigene Meßgerätesteuerungen damit gebaut.
Die -Wl,--entry= Option ist eine gas Anweisung? Ach so, ne, ld.
Besagt, daß kein Entrypoint erzeugt wird?
OK, das ist eine FORTH-Umgebung. Gut, das ist natürlich dann nochmal was
anderes.
Habe ich lange nicht mehr in den Fingern gehabt, das letzte Mal wohl
beim FreeBSD-Bootloader.
Christoph K. schrieb:> Die -Wl,--entry= Option ist eine gas Anweisung?
Nein, das war jetzt für den GCC. "-Wl" besagt dabei, dass er das
nachfolgende direkt an den Linker weitergeben soll, "--entry" ist eine
Anweisung für den Linker.
> Besagt, daß kein Entrypoint erzeugt wird?
Ja. :)
Eigentlich sollst du natürlich nach dem --entry= das Symbol für den
Startpunkt angeben, denn das nimmt der GDB beim Start der Firmware nach
einem Reset.
Jörg W. schrieb:
...
> Eigentlich sollst du natürlich nach dem --entry= das Symbol für den> Startpunkt angeben, denn das nimmt der GDB beim Start der Firmware nach> einem Reset.
Das wäre in meinem Fall das Label "Reset" bei 0x3a56.
Sollte ich das vielleicht dem ld mitteilen?
Woher nimmt er denn den SP, wenn der Bootprozeß nicht hardwaremäßig
verläuft?
Christoph K. schrieb:> Woher nimmt er denn den SP, wenn der Bootprozeß nicht hardwaremäßig> verläuft?
SP setzen ist üblicherweise der erste Befehl im Reset handler vom
startup code:
1
Reset_Handler:
2
ldr sp, =_estack /* set stack pointer */
Und das _estack in diesem Fall kommt aus dem Linkerscript.
Christoph K. schrieb:> Das wäre in meinem Fall das Label "Reset" bei 0x3a56.> Sollte ich das vielleicht dem ld mitteilen?
Ja.
> Woher nimmt er denn den SP, wenn der Bootprozeß nicht hardwaremäßig> verläuft?
Gute Frage. :-)
1
(gdb) mon reset halt
2
(gdb) load
3
Loading section .text, size 0x17214 lma 0x0
4
Loading section .ARM.exidx, size 0x8 lma 0x17214
5
Loading section .relocate, size 0xa80 lma 0x1721c
6
Start address 0x29e8, load size 97436
7
Transfer rate: 9 KB/sec, 12179 bytes/write.
8
(gdb) si
9
0x000029ea in Reset_Handler () at board_cstartup_gnu.c:210
Der kommt offenbar tatsächlich aus der Vektortabelle. Frag mich nicht,
auf welche Weise. ;-)
Das mit dem entry point im ELF-File war mir nur aufgefallen, weil wir
das anfangs nicht hatten, und dann startete der Debugger immer bei 0 …
(Das hier ist jetzt kein STM32, aber ein anderer Cortex-M.)
Johannes S. schrieb:> und es ist der erste Eintrag in der Vectortabelle
Das ist schon klar.
Aber es ist nicht klar, warum der Debugger mit dem entsprechend
gesetzten SP startet, während er es nicht schafft, den PC aus dem
zweiten Eintrag der Tabelle zu entnehmen, sondern stattdessen mit dem
startet, was als Entry im ELF-File steht (oder eben mit 0, wenn es dafür
nichts gibt).
Vermutlich wird der SP durch die Hardware bereits gesetzt (so wie der PC
auch beim "richtigen" Booten), aber der PC wird in diesem Falle stets
durch den Debugger vorgegeben.
Bin zu faul, mir jetzt die Kommunikation zwischen Debugger und
GDB-Server dahingehend anzusehen.
Jörg W. schrieb:> Christoph K. schrieb:>>> Das wäre in meinem Fall das Label "Reset" bei 0x3a56.>> Sollte ich das vielleicht dem ld mitteilen?>> Ja.>>> Woher nimmt er denn den SP, wenn der Bootprozeß nicht hardwaremäßig>> verläuft?>> Gute Frage. :-)>>
>> Der kommt offenbar tatsächlich aus der Vektortabelle. Frag mich nicht,> auf welche Weise. ;-)>> Das mit dem entry point im ELF-File war mir nur aufgefallen, weil wir> das anfangs nicht hatten, und dann startete der Debugger immer bei 0 …>> (Das hier ist jetzt kein STM32, aber ein anderer Cortex-M.)
Vergleich mit meiner Situation:
1
$ ./GDB
2
GNU gdb (GNU Tools for Arm Embedded Processors 8-2018-q4-major) 8.2.50.20181213-git
3
Copyright (C) 2018 Free Software Foundation, Inc.
4
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
5
This is free software: you are free to change and redistribute it.
6
There is NO WARRANTY, to the extent permitted by law.
7
Type "show copying" and "show warranty" for details.
8
This GDB was configured as "--host=x86_64-apple-darwin10 --target=arm-none-eabi".
9
Type "show configuration" for configuration details.
10
For bug reporting instructions, please see:
11
<http://www.gnu.org/software/gdb/bugs/>.
12
Find the GDB manual and other documentation resources online at:
13
<http://www.gnu.org/software/gdb/documentation/>.
14
15
For help, type "help".
16
Type "apropos word" to search for commands related to "word".
17
0x0000161c in uart_init ()
18
Unable to match requested speed 2000 kHz, using 1800 kHz
19
Unable to match requested speed 2000 kHz, using 1800 kHz
20
target halted due to debug-request, current mode: Thread
21
xPSR: 0x01000000 pc: 0x00003a56 msp: 0x20000330
22
Loading section .text, size 0x3bc8 lma 0x0
23
Start address 0x0, load size 15304
24
Transfer rate: 122432 bits in <1 sec, 15304 bytes/write.
25
(gdb) si
26
halted: PC: 0x00000002
27
0x00000002 in ?? ()
28
(gdb)
Ich hatte jetzt auch noch mal im Makefile dem Linker versucht, den entry
point mitzuteilen:
Jörg W. schrieb:> Christoph K. schrieb:>> Da ändert sich nichts an der Start address 0x0.>> Das ist allerdings seltsam.>> Bei uns steht der entry point im Linkerscript:>>
>> -e sollte aber eigentlich trotzdem klappen.>> Alternativ kannst du den PC auch im GDB direkt setzen:>>
1
> (gdb) set $pc=Reset
2
>
>> Wofür brauchst du denn überhaupt das .bin-File?
Das bin-File wurde vom Autor des mecrisp-Forth benutzt zum flashen. Den
ELF-file hatte ich aus dem Make herausgefischt (siehe auskommentierte
Zeile im clean:), um es im gdb verwenden zu können.
Ich soll Zitatzeilen löschen. Vielleicht klappt's auch, wenn ich mehr
Gehalt hinzufüge :)
>>>> -e sollte aber eigentlich trotzdem klappen.>>>> Alternativ kannst du den PC auch im GDB direkt setzen:>>>>
1
>> (gdb) set $pc=Reset
2
>>
Aber (!):
1
$ arm-none-eabi-nm firmware.elf | grep Reset
2
00003a56 t Reset
3
U Reset
U - Unknown?
Ach so, das war jetzt die Folge des nicht aufgelösten -e Reset in der
Linkerzeile. Wegnehmen desselben nimmt das U auch weg.
Gelöst: Füge ein .global Reset in den Assembler Code ein. Dann wird das
"t Reset" zum "T Reset". Das müssen wir Unix-Hasen doch wissen :)
Christoph K. schrieb:> Gelöst: Füge ein .global Reset in den Assembler Code ein. Dann wird das> "t Reset" zum "T Reset".
Yup, das wollte ich auch gerade anmerken.
Christoph K. schrieb:> Das bin-File wurde vom Autor des mecrisp-Forth benutzt zum flashen.
Muss man nicht haben. Das ELF-File geht genauso, kann OpenOCD problemlos
benutzen (und auch AVRDUDE für AVRs inzwischen seit vielen Jahren).
.bin-Files bauen wir nur noch für Onboard-Bootloader (Laden von Firmware
beim Kunden direkt von SD-Karten). Da wäre das Parsen eines ELF-Files
bissel aufwändig.