Eigenes new klappt nicht bei Konstruktor mit Speicherallokation

Gast #2230071
Lesenswert?

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
Angehängte Dateien:
Gast #2230279
Lesenswert?

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
Angehängte Dateien:
Gast #2231493
Lesenswert?

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.
???!
Gast #2231581
Lesenswert?

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 = new char[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
Angehängte Dateien:
Gast #2236748
Lesenswert?

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
Gast #2236781
Lesenswert?

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) new B;

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.
Gast #2236807
Lesenswert?

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.
Gast #2236814
Lesenswert?

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
Gast #2237534
Lesenswert?

Also, nun ist es vollbracht.
@Rolf: danke - Dein Tip hat den richtigen Weg aufgezeigt!

Ich die __get_MSP(void) Funktion ersetzt durch:
1
uint32_t __get_MSP(void)
2
{
3
 register uint32_t result __asm ("r0") = 0;
4

5
__asm volatile ("MRS %0, msp\n"
6
                  "BX  lr     \n"  : "=r" (result) );
7
 return(result);
8
}

aus 
http://cdn.energymicro.com/dl/documentation/doxygen/core__cm3_8c-source.html.
Damit hat es funktioniert.
Zum Vergleich die alte Funktion, zusammen mit der Header-Info der 
core_cm3.c:
1
/**************************************************************************//**
2
 * @file     core_cm3.c
3
 * @brief    CMSIS Cortex-M3 Core Peripheral Access Layer Source File
4
 * @version  V1.30
5
 * @date     30. October 2009
6

7
...
8

9
 * Return the current value of the MSP (main stack pointer)
10
 * Cortex processor register
11
 */
12
uint32_t __get_MSP(void) __attribute__( ( naked ) );
13
uint32_t __get_MSP(void)
14
{
15
  uint32_t result=0;
16

17
  __ASM volatile ("MRS %0, msp\n\t" 
18
                  "MOV r0, %0 \n\t"
19
                  "BX  lr     \n\t"  : "=r" (result) );
20
  return(result);
21
}

Grüße
schnack

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