Forum: Mikrocontroller und Digitale Elektronik STM32F4 C++ springt bei __libc_init_array in den HardFault


von Nils H. (irrenhaus)


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

von Gar Nix (Gast)


Lesenswert?

Würde dir ja gerne helfen, aber mit den Code-Schnipseln kann man rein 
Gar Nix anfangen.

von Nils H. (irrenhaus)


Angehängte Dateien:

Lesenswert?

Die waren eigentlich nur zum Verdeutlichen meiner Aussagen gedacht :)

Ich weiß nicht so recht, wie ich das Problem eingrenzen soll, daher habe 
ich nicht mehr Code angehängt.

Das hole ich hiermit nach.

Vielen Dank und
MfG

von Roland H. (batchman)


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.

von Roland H. (batchman)


Lesenswert?

Ach ja, und probiere es mal ohne RTOS

von (prx) A. K. (prx)


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.

von Nils H. (irrenhaus)


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.

von Jim M. (turboj)


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.

von Nils H. (irrenhaus)


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

von (prx) A. K. (prx)


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?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.