Hi,
auf einem kleinen C++ - Projekt (ich verwende CodeSourcery g++) für den
STM32F103RE habe ich den Versuch unternommen, einen eigenen globalen
new-Handler zu schreiben (um nicht die Standard Libs einbinden zu
müssen).
Bislang klappt das Allokieren ganz gut. Allerdings klappt das Allokieren
von Speicher im Konstruktor schlicht nicht.
Ich habe das Problem in einem Beispielprogramm rekonstruiert (main.cpp).
Das RAM liegt beim STM32F103RE im Bereich $20000000 bis $2000ffff. Die
Allokation für myA klappt, für myB liefert er mir einen Zeiger vom Stack
:-(
-> Screen Shot: main_debug.jpg
Wenn man den Konstruktor von B durchgeht, sieht man, daß das Objekt
offenbar richtig allokiert wurde (this) und auch für myVector die
Allokation geklappt hat. Nur beim Rücksprung geht die Info über this
anscheinend wieder "verloren".
-> Screen Shot: main_debug2.jpg
Die Speicherallokation selbst wird in mini_cpp.cpp vorgenommen.
Aber warum? Kann jemand helfen?
Grüße
schnack
Die Allokation klappt genau dann, wenn sie separat z.B. in einer init()
Methode erfolgt.
Letztlich ist mir unklar, wie in einem new der Zeiger auf den
allokierten Speicher zurückgegeben wird, wenn nach der Allokation noch
der Konstruktor durchlaufen wird, bevor der Rücksprung erfolgt.
Grüße
schnack
Ich könnte mir vorstellen, dass alles ordentlich funktioniert.
Vielleicht ist es nur eine Fehlinterprtation?
C. D. schrieb:> daß das Objekt> offenbar richtig allokiert wurde (this) und auch für myVector die> Allokation geklappt hat. Nur beim Rücksprung geht die Info über this> anscheinend wieder "verloren".
Nach dem Rücksprung aus dem Konstruktor ist es ja nicht mehr 'this',
dann ist es 'B'. 'this' ist dann was anderes.
???!
Hallo Ralf,
ich fürchte, daß es keine Fehlinterpretation ist. Das Programm main.cpp
ist auch nur die kondensierte Fassung des Problems. Tatsächlich haben
sich die Probleme im eigentlichen Projekt beim Aufruf virtueller
Funktionen gezeigt, die überhaupt nicht funktionierten (führten auf dem
STM32 direkt zu einem 'Hard Fault').
Nach langer Sucherei habe ich dann gemerkt, daß der Aufruf für
diejenigen Objekte fehlschlägt, bei denen das new() nicht funktioniert
hat (weil sie im Konstruktor eigenen Speicher vom Heap allokieren) und
dann dieses Kurzbeispiel zusammengesetzt.
Ich habe inzwischen herausbekommen, wie der Wert für den Zeiger myB zu
interpretieren ist: es ist die untere, aktuelle Stack-Grenze.
Der Screen-Shot zeigt den Durchlauf durch p_malloc(), aufgerufen in
1
myVector=newchar[10];
. Der Wert für stack ist $2000ff94. Und genau diesen Wert hat nach dem
Rücksprung auch myB.
Wobei: warum das so ist, ist mir ein völliges Rätsel..
Grüße
schnack
Hi,
ich denke 'volatile' kann da nicht helfen (habs aber trotzdem
ausprobiert- ohne Verbesserung), denn hier gibt es ja keine ISR's oder
andere Funktionen, die "von der Seite reingrätschen".
Meine "Lösung" besteht in dem workaround von oben, d.h. ich allokiere
Speicher separat.
Das ist keine wirklich erfreuliche Sache, denn ich kann auf diese Weise
im eigentlichen Projekt die Allokation durch Vererbung NICHT automatisch
erledigen lassen -> Ideal wäre es, wenn der Konstruktor der Oberklasse
automatisch durchläuft und dabei den Speicher allokiert.
So muß ich die Allokation durch die Oberklasse-Methode selbst aufrufen
(was ich tue) oder in der Unterklasse das Ganze noch einmal
implementieren.
Grüße
schnack
Hm, das verstehe ich nicht. Ist das ein anderer workaround?
Mit placement-new übergebe ich die bereits die Speicheradresse - aber
woher soll die kommen?
1
// woher kommt myBAddress?
2
myB=new(myBAddress)newB;
Zum anderen: die Allokation klappt ja "weitgehend" - nur beim Rücksprung
aus new() wird die Adresse verbaselt. Ich fände es sauberer, die Ursache
für das Problem zu verstehen und dann nach einer geeignete Lösung zu
suchen.
Weiß jemand, was ein C++ Compiler mit der Adresse für den allokierten
Speicher macht, wenn er vor dem Rücksprung die Konstuktoren durchläuft?
"Eigentlich" sollte er diese Adresse via Stack zurückgeben - wie das bei
Funktionen üblich ist.
Meine Vermutung ist deshalb, daß irgendetwas mit dem Stack nicht mehr
stimmt. Denn in p_malloc() wird die Variable stack:
1
char*stack=(char*)__get_MSP();
angelegt - mit dem Wert des aktuellen Stackzeigers - geliefert von
__get_MSP(). Diese Variable liegt ja ihrerseits auch auf dem Stack
(sorry, ist schon verwirrend).
Offenbar wird dieser Variableninhalt als Rückgabewert für new()
verwendet.
Hm.
Ich habe jetzt die Allokation etwas verändert und die
Sicherheitsabfrage, ob heap und stack zusammengelaufen sind,
ausgelassen.
Und prompt klappt es...
Das ist natürlich auch noch keine Lösung, aber immerhin..
Grüße
schnack
C. D. schrieb:> Hm, das verstehe ich nicht. Ist das ein anderer workaround?
Ja. Es ist die Art, die C++ vorsieht, um die Allokation von der
Initialisierung zu trennen. Du allokierst erst deinen Speicher und
übergibst den an placement-new. Das ruft dann nur noch die Konstruktoren
auf.
> Mit placement-new übergebe ich die bereits die Speicheradresse - aber> woher soll die kommen?
Von deinem p_malloc.
> Zum anderen: die Allokation klappt ja "weitgehend" - nur beim Rücksprung> aus new() wird die Adresse verbaselt. Ich fände es sauberer, die Ursache> für das Problem zu verstehen und dann nach einer geeignete Lösung zu> suchen.
Natürlich, aber das scheint ja nicht so gut zu klappen, da du oben
deinen Workaround mit der init()-Funktion beschrieben hast.
placement-new wäre auch ein Workaround, aber einer, der vielleicht nicht
ganz so unkomfortabel ist.
> Weiß jemand, was ein C++ Compiler mit der Adresse für den allokierten> Speicher macht, wenn er vor dem Rücksprung die Konstuktoren durchläuft?
Was soll er damit schon machen? Er übergibt ihn als this-Zeiger an den
Konstruktor.
> "Eigentlich" sollte er diese Adresse via Stack zurückgeben - wie das bei> Funktionen üblich ist.
Meistens wird das eher über Register gemacht.
> Meine Vermutung ist deshalb, daß irgendetwas mit dem Stack nicht mehr> stimmt. Denn in p_malloc() wird die Variable stack:> char* stack = (char*) __get_MSP();> angelegt - mit dem Wert des aktuellen Stackzeigers - geliefert von> __get_MSP().
Bist du eigentlich sicher, daß __get_MSP() korrekt funktioniert? Ich
kenne mich mit dem Prozessor nicht aus, aber es sieht für mich aus, als
ob der asm-Teil das Register r0 verändert, ohne das dem Compiler
mitzuteilen.
> Diese Variable liegt ja ihrerseits auch auf dem Stack (sorry, ist schon
verwirrend).
Ich würde auch hier erwarten, daß sie in einem Register liegt.
Rolf Magnus schrieb:>> Meine Vermutung ist deshalb, daß irgendetwas mit dem Stack nicht mehr>> stimmt. Denn in p_malloc() wird die Variable stack:>> char* stack = (char*) __get_MSP();>> angelegt - mit dem Wert des aktuellen Stackzeigers - geliefert von>> __get_MSP().>> Bist du eigentlich sicher, daß __get_MSP() korrekt funktioniert? Ich> kenne mich mit dem Prozessor nicht aus, aber es sieht für mich aus, als> ob der asm-Teil das Register r0 verändert, ohne das dem Compiler> mitzuteilen.
Nein, bin ich mir nicht. Ich habe die Funktion __get_MSP() aus
core_cm3.c quasi blind übernommen. Wahrscheinlich liegt hier der Hund
begraben..
Wenn r0 natürlich für den Rückgabewert benutzt wird, ist das nicht so
pfiffig.
Grüße
schnack
Rolf Magnus schrieb:> als> ob der asm-Teil das Register r0 verändert, ohne das dem Compiler> mitzuteilen.
Wie kann ich das dem Compiler mitteilen?
Grüße
schnack