STM32F4 C++ springt bei __libc_init_array in den HardFault

#2689417
Lesenswert?

Hi,

ich entwickle gerade ein Programm für einen STM32F407VG unter Verwendung 
von C++.

Hierfür nutze ich eine selbst kompilierte GCC Toolchain mit der newlib.

So weit funktioniert alles. Das Problem was ich habe kam, als ich 
festgestellt habe, dass die Konstruktoren von globalen Variablen nicht 
aufgerufen werden, also z.B.:
1
Color colorBlack(0x00, 0x00, 0x00);

Also habe ich ein bisschen gesucht und irgendwann gemerkt, dass in 
meiner startup Assembler Datei kein Aufruf von __libc_init_array 
erfolgte.

Also kurzerhand hinzugefügt:
1
[....]
2
  /*enable fpu begin*/
3
  ldr     r0, =0xe000ed88           /*; enable cp10,cp11 */
4
  ldr     r1,[r0]
5
  ldr     r2, =0xf00000
6
  orr     r1,r1,r2
7
  str     r1,[r0]
8
  /*enable fpu end*/
9

10
/* Call the clock system intitialization function.*/
11
  bl  SystemInit
12

13
/* Call static constructors */
14
  bl __libc_init_array
15
/* Call the application's entry point.*/
16
  bl  main
17
  bx  lr

In der Linker-Datei sind die Definitionen für __preinit_array_start/end, 
__init_array_start/end und __fini_array_start/end enthalten.

Das Problem was ich nun habe, ist, dass der Code zwar ohne Probleme 
kompiliert, der STM32 aber (beim Durchsteppen mit dem Debugger) beim 
Jump nach __libc_init_array in den HardFault_Handler springt (kurzzeitig 
sah es auch mal danach aus, dass es der BusFault_Handler wäre, hier bin 
ich mir nicht sicher).

Hat schonmal jemand eine solche Sache gehabt?
Liegt das eher an meiner Toolchain oder eher an meinem Code? Ich habe es 
zwischenzeitlich mal mit der Sourcery CodeBench Toolchain kompiliert, 
hier war das gleiche, insofern hatte ich die Toolchain erstmal 
ausgeschlossen...

Ich wäre um Tipps sehr dankbar.

Vielen Dank und
MfG
Nils
#2691248
Lesenswert?

Unter der folgenden URL findest Du ein Assembler-Startup, welcher es 
"direkt" erledigt. Der relevante Teil ist bei mir mit stm32f407 im 
Einsatz:

http://tech.munts.com/MCU/Frameworks/ARM/stm32f4/stm32f407vg.S

Für stm32f100 verwende ich einen C-Startup mit einer "abgespeckten" 
__libc_init_array - Variante: Den Aufruf von _init() habe ich daraus 
entfernt.

Beides funktioniert.

Warum der Aufruf von __libc_init_array bei Dir aus Assembler heraus 
nicht funktioniert, kann ich nicht sagen.
#2691263
Lesenswert?

Schau dir mal die Adresstabellen an, die bei __[pre]init_array_start 
anfangen. Ob das korrekte adressierte und als Thumb-Code getaggte 
Pointer sind.

Was man gerne falsch macht: Die Reihenfolge von Konstruktoraufrufen ist 
undefiniert, auch fehlen da noch diverse andere Initialisierungen. Man 
sollte also gut aufpassen, was man in Konstruktoren reinschreibt. Viel 
mehr als Initialisierungen von Instanzvariablen sollte das nicht sein.

Statt den Startup-Code zu hacken kannst du diese Funktion ebenso gut am 
Anfang von main() aufrufen.
#2691682
Lesenswert?

Hallo,

vielen Dank für die Antworten.

@Roland H.
- Mit dem RTOS wollte ich grade ein wenig rumspielen, das ist 
rausgeflogen zur Sicherheit, daran lags aber nicht.
- Vielen Dank für den Link, leider funktioniert es damit auch nicht. 
Welchen Assembler verwendest du denn? Mein gcc-as meckert da schon über 
die Kommentare ^^

@A.K.
Nachdem ich so weit alle globalen Sachen rausgenommen hatte (alle für 
die ein selbstgeschrieber Konstruktor von nöten wäre), sind in dem Array 
2 Einträge.
Wie finde ich denn heraus, ob es korrekt getaggte Thumb-Pointer sind?
Der eine Eintrag zeigt auf die Funktion frame_dummy.
Der andere ist ein wenig komisch:
Er zeigt auf die Adresse 0x08001551. Da gibt es aber nichts. Das fällt 
rein prinzipiell in eine Funktion "_GLOBAL__sub_I_andale_mono" die an 
Adresse 0x08001550 liegt (andale_mono ist ein globales uint8_t Array in 
dem eine meiner Fonts liegt):
1
08001550 <_GLOBAL__sub_I_andale_mono>:
2
 8001550:  b580        push  {r7, lr}
3
 8001552:  af00        add  r7, sp, #0
4
 8001554:  f04f 0001   mov.w  r0, #1
5
 8001558:  f64f 71ff   movw  r1, #65535  ; 0xffff
6
 800155c:  f7ff ffce   bl  80014fc <_Z41__static_initialization_and_destruction_0ii>
7
 8001560:  bd80        pop  {r7, pc}
8
 8001562:  bf00        nop

Wie man sieht wird hier die Adresse 0x08001551 "übersprungen". Dass das 
für mich falsch aussieht, kann aber auch daran liegen, dass ich keine 
Erfahrung mit der Art von Fehlersuche und Disassemblierungen habe.

Ich habe mittlerweile das ganze mal weiter geführt, indem ich am Anfang 
meiner main Funktion folgendes mache:
1
uint32_t count = __init_array_end - __init_array_start;
2

3
uint32_t i;
4
//_init();
5
for(i = 0; i < count; i++) {
6
  __init_array_start[i]();
7
}

Auf gut Deutsch, ich gehe manuell durch das Array von Konstruktoren und 
rufe jeden einzelnen auf.

Folgendes passiert: Die zwei Einträge werden richtig gezählt, er läuft 
in die Schleife und sichert sich die Adresse der ersten Funktion 
(frame_dummy). Dann springt er per bx dort hin. So weit funktioniert 
alles. Sobald aber die erste Instruktion dieser Funktion ausgeführt wird 
(push {r3, lr}) springt er in den HardFault_Handler.

Nebenbei: Ich hatte zwischenzeitlich testweise ein paar globale 
Klasseninstanzen mit Konstruktor erzeugt. Hier hatte ich komischerweise 
keine Änderung an dem init_array (auch nicht mit explizitem extern vor 
den Variablen).

Hat jemand noch eine Idee?

Vielen Dank

PS: Die newlib hat laut 
http://newlib.sourcearchive.com/documentation/1.14.0/init_8c-source.html 
einen Aufruf an _init() vor dem Konstruktoren Aufruf. Wenn ich den mit 
rein nehme, springt er hier schon in den HardFault_Handler. Hier führt 
er nicht mal den Sprung aus.
#2691687
Lesenswert?

> Wie finde ich denn heraus, ob es korrekt getaggte Thumb-Pointer sind?

Das letzte Bit muss gesetzt sein, d.h. die Addresse ist immer ungrade. 
Sonst ist es kein Thumb Pointer, sondern einer für den ARM Modus, den 
der Cortex M3 nicht kennt und dann einen (Usage?) Fault erzeugt.

> Wie man sieht wird hier die Adresse 0x08001551 "übersprungen".

NACK. Das niederwertigste Bit wird ausmaskiert, da es den Prozessormodus 
vorgibt, s.o. Man sollte sich schon die Doku zum Prozessor Core 
anschauen, wenn man Disassembler Listings verstehen will. Die Addresse 
des tatsächlich ausgeführten Codes ist also korrekt "0x08001550". Nur 
darf man die wie oben erwähnt nicht ohne das unterste Bit in das PC 
Register laden.
#2691691
Lesenswert?

Jim Meba schrieb:
>> Wie finde ich denn heraus, ob es korrekt getaggte Thumb-Pointer sind?
>
> Das letzte Bit muss gesetzt sein, d.h. die Addresse ist immer ungrade.
> Sonst ist es kein Thumb Pointer, sondern einer für den ARM Modus, den
> der Cortex M3 nicht kennt und dann einen (Usage?) Fault erzeugt.
>
>> Wie man sieht wird hier die Adresse 0x08001551 "übersprungen".
>
> NACK. Das niederwertigste Bit wird ausmaskiert, da es den Prozessormodus
> vorgibt, s.o. Man sollte sich schon die Doku zum Prozessor Core
> anschauen, wenn man Disassembler Listings verstehen will. Die Addresse
> des tatsächlich ausgeführten Codes ist also korrekt "0x08001550". Nur
> darf man die wie oben erwähnt nicht ohne das unterste Bit in das PC
> Register laden.

Und wieder was dazu gelernt, vielen Dank :)

Nur damit ich das richtig verstehe: Die Adresse der ersten Funktion 
(frame_dummy) im init_array ist "0x080001c8", d.h. es ist Code für den 
ARM Modus und nicht Thumb-Modus Code? Was dann mein Problem erklären 
dürfte?

Vielen Dank
#2691703
Lesenswert?

Nils Hesse schrieb:

> Nur damit ich das richtig verstehe: Die Adresse der ersten Funktion
> (frame_dummy) im init_array ist "0x080001c8", d.h. es ist Code für den
> ARM Modus und nicht Thumb-Modus Code? Was dann mein Problem erklären
> dürfte?

Ja, das dürfte es. Wenn Bit 0 des Pointers nicht gesetzt ist, dann kann 
der Prozessor das nicht ausführen und landet auf einem Fault. Wurde 
dieser Code versehentlich für ARM-Modus übersetzt?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren