Hallo,
ich habe hier einen Code, welcher immer abstürzt
1
staticuint32table_p[64];
2
statictable_T*resp_table;
3
4
typedefstruct
5
{
6
uint8inUse;
7
uint8status;
8
...
9
}table_T,*table_t;
10
11
typedefstruct
12
{
13
...
14
uint32*table;
15
uint32size;
16
...
17
}init_ts
18
19
20
voidinit(void)
21
{
22
23
init_tsinit_s;
24
...
25
init_s.table=table_p;
26
init_s.size=64;
27
...
28
}
29
30
31
staticvoidinstall_tab(init_ts*init_s)
32
{
33
resp_table=(table_t)(init_s->table);
34
for(uint32n=0;n<init_s->size;n++)
35
{
36
resp_table[n].inUse=0;// hier crasht es. Im Debugger (qt-creator) sehe ich aber, dass die Adresse von resp_table = die Adresse von table_p ist
37
resp_table[n].status=0;
38
}
39
}
Im Debugger steht
table_p @0x6ae0e0 [uint32]64
resp_table 0x6ae0e00000000 table_T*
resp_table liegt an der Adresse 0x6a21d0, beinhaltet aber den oben
beschriebenen Wert.
Als Fehler bekomme ich segmentation fault. Was ja heißt, dass in einem
falschen Bereich geschrieben wurde.
Woran kann das liegen?
Hmm... das compiliert nicht...
marcel schrieb:> Als Fehler bekomme ich segmentation fault. Was ja heißt, dass in einem> falschen Bereich geschrieben wurde.
Schau mal in Zeile 42. Da ist der Übeltäter.
Jup du versuchst eine Struktur auf einen uint32 Pointer zu drücken.
Deine Struktur ist im Stack aber nicht zusammenhängend angelegt. Du
greifst unerlaubt in den Speicher..
Dein Cast ist falsch und das ganze macht so gesehen auch keinen Sinn....
Der Code ist unübersichtlich und was eigentlich passieren soll ist
unklar.
B. W. schrieb:> Hmm... das compiliert nicht...
Der ziemlich zusammengestückelte und fehlerhafte Code lässt auch darauf
schießen, dass das nicht der Code ist, der das beobachtete Problem
verursacht hat. So kann man damit wenig anfangen. Besser wäre ein
minimales, aber vollständiges Programm, das du genau so ausgeführt
hast und das den Fehler zeigt.
Marco H. schrieb:> Dein Cast ist falsch und das ganze macht so gesehen auch keinen Sinn....
Dachte ich zuerst auch, aber ist er nicht. Man achte beim typedef
darauf, dass die Struktur selbst table_T mit großem T heißt, während
table_t einen Zeiger darauf bezeichnet. Ziemlich heimtückische Falle.
Meine Glaskugel meint, es könnte sein, dass ein table_T größer ist als
ein uint32, und daher in ein Array aus 64 uint32 keine 64 table_T
reinpassen.
Rolf M. schrieb:> Dachte ich zuerst auch, aber ist er nicht. Man achte beim typedef> darauf, dass die Struktur selbst table_T mit großem T heißt, während> table_t einen Zeiger darauf bezeichnet. Ziemlich heimtückische Falle.> Meine Glaskugel meint, es könnte sein, dass ein table_T größer ist als> ein uint32, und daher in ein Array aus 64 uint32 keine 64 table_T> reinpassen.
Das ist relativ egal, da es eh undefined behavior ist (verstößt gegen
die "strict aliasing" Regeln)
Wie schon geschrieben wurde, gibt es einen Array-Überlauf, wenn
sizeof(table_T) > sizeof(uint32) ist.
Die Zeile
1
resp_table=(table_t)(init_s->table);
verstößt gegen die strict aliasing Rule, d.h. selbst wenn es keinen
Array-Überlauf gibt, ist das Verhalten dennoch undefiniert.
Es ist nicht klar, ob init_s in dieser Zeile überhaupt initialisiert
ist. Es gibt zwar die Funktion init(), die evtl. auch irgendwo
aufgerufen wird, aber darin wird sinnloserweise nur eine lokale Variable
initialisiert.
Yalu X. schrieb:> Die Zeile>> resp_table = (table_t)(init_s->table);>> verstößt gegen die strict aliasing Rule, d.h. selbst wenn es keinen> Array-Überlauf gibt, ist das Verhalten dennoch undefiniert.
Genau genommen sorgt erst die Zeile
1
resp_table[n].inUse=0;
für ub ;-) Die Konvertierung des Pointers ist erlaubt, der Zugriff
nicht.
mh schrieb:> Genau genommen sorgt erst die Zeile> resp_table[n].inUse = 0;> für ub ;-) Die Konvertierung des Pointers ist erlaubt, der Zugriff> nicht.
Richtig, die Pointer-Konvertierung war nur die Vorbereitung zum Unheil.
marcel schrieb:> static table_T *resp_table;>> typedef struct> {> uint8 inUse;> uint8 status;> ...> } table_T, *table_t;
Kann man überhaupt eine Variable vom Typ table_T erzeugen bevor sie
überhaupt definiert wurde? Das ist doch auch falsch.
marcel schrieb:> void init(void)> {>> init_ts init_s;
Du initialisierst hier eine lokale Variable, die dann wieder
verschwindet. Dann verwendest du einen Pointer auf diese. Das geht
nicht.
BTW ein compilierendes Beispiel mit Warnings wuerde helfen.
leo
leo schrieb:> Du initialisierst hier eine lokale Variable, die dann wieder> verschwindet.
Korrekt.
> Dann verwendest du einen Pointer auf diese.
Nein, es wird ein Pointer auf eine globale Variable sein, die
zufälligerweise genauso heisst. Sonst würde das gar nicht durch den
Compiler gehen.
Frank M. schrieb:> leo schrieb:>> Du initialisierst hier eine lokale Variable, die dann wieder>> verschwindet.>> Korrekt.>>> Dann verwendest du einen Pointer auf diese.>> Nein, es wird ein Pointer auf eine globale Variable sein, die> zufälligerweise genauso heisst. Sonst würde das gar nicht durch den> Compiler gehen.
Ich verstehe nicht so ganz worauf ihr euch bezieht. In init gibt es eine
lokale Variable init_s. Die Funktion install_tab hat einen Parameter
init_s. Da sehe ich keine Probleme. Ok es ist unklar, wann welche
Funktion aufgerufen wird, wann das Programm abstürzt und welchen Zustand
die debug-Ausgabe abbildet. Aber so wie es aussieht gehe ich von
folgendem Ablauf aus:
init erzeugt ein init_ts mit Namen init_s und "initialisiert" es mit
einem Pointer auf das globale table_p. Weiterhin in init wird dann
install_tab mit einem Pointer auf das lokale init_s aufgerufen.
install_tab schreibt dann über den Pointer im init_s Parameter in das
globale table_p.
Das sollte funktionieren, wenn man das undefined behavior beseitigt.
mh schrieb:> Ich verstehe nicht so ganz worauf ihr euch bezieht. In init gibt es eine> lokale Variable init_s. Die Funktion install_tab hat einen Parameter> init_s.
Die eine Variable init_s hat aber mit der anderen init_s - wo auch immer
die herkommt - nichts zu tun. An die lokale Variable kommt man von
außerhalb nicht dran, darum kümmert sich der Compiler. Oder TO hat
seinen Code soweit zurechtgestutzt, dass der präsentierte Code nichts
mehr mit dem tatsächlichen Code zu tun hat.
Naja ganz weg ist sie ja nicht, sie ist im Stack noch vorhanden. Bis sie
überschrieben wird. Den Pointer von der Struktur init_ts die der
Funktion install_tab() übergeben wird muss ja wo her kommen?
Frank M. schrieb:> mh schrieb:>> Ich verstehe nicht so ganz worauf ihr euch bezieht. In init gibt es eine>> lokale Variable init_s. Die Funktion install_tab hat einen Parameter>> init_s.>> Die eine Variable init_s hat aber mit der anderen init_s - wo auch immer> die herkommt - nichts zu tun. An die lokale Variable kommt man von> außerhalb nicht dran, darum kümmert sich der Compiler. Oder TO hat> seinen Code soweit zurechtgestutzt, dass der präsentierte Code nichts> mehr mit dem tatsächlichen Code zu tun hat.
Natürlich hat er ihn zusammengestutzt. Deswegen die ganzen "...".
Ansonsten würde es ja auch keine Probleme geben, weil init und
install_tab nicht aufgerufen werden. install_tab wird IN init
aufgerufen.
mh schrieb:> install_tab wird IN init aufgerufen.
Ah, das könnte des Rätsels Lösung sein. Danke für den Hinweis. Das zeigt
aber mal wieder, dass ein nicht-übersetzbarer Code einfach nichts taugt.
Frank M. schrieb:>> Dann verwendest du einen Pointer auf diese.>> Nein, es wird ein Pointer auf eine globale Variable sein,
Ja eh, aber das geht aus dem gezeigten Code-Geschwafel nicht so klar
hervor.
leo
Lothar schrieb:> Marco H. schrieb:>> Deine Struktur ist im Stack aber nicht zusammenhängend angelegt>> PC oder uC?>> __packed struct>> https://www.keil.com/support/man/docs/armcc/armcc_chr1359124968737.htm
Klar kann man den Compiler anweisen die Struktur so auf dem Stack zu
packen. Sehr beliebt bei AVRs weil es so schön einfach ist, mit OS
sollte man dies aber vermeiden.
Gerade Anfängern ist die Tragweite nicht bewusst und dann schlägt man so
etwas erstmal nicht vor. Da es das eigentliche Problem nicht behebt.
Marco H. schrieb:> Compiler anweisen die Struktur so auf dem Stack
Mit OS sollte es doch mit union gehen. Habe hier einen Message Block mit
256 Byte der als Byte Pointer übergeben werden muss, aber auch Variablen
enthält, auf die direkt zugegriffen werden kann.
> Sehr beliebt bei AVR
Bei AVR sind eher nicht-aligned struct beliebt z.B. bei Ethernet oder SD
Treiber. Portierung auf Cortex-M gibt dann erst mal Hardfault.
Die Anfrage mutet ein wenig wie folgende Aufgabe an:
Setze den unvollständigen Haufen einzelner Scherben zur ursprünglichen
Vase zusammen und finde anschließend die Undichtigkeit in derselben ;-)
Yalu X. schrieb:> Die Anfrage mutet ein wenig wie folgende Aufgabe an:>> Setze den unvollständigen Haufen einzelner Scherben zur ursprünglichen> Vase zusammen und finde anschließend die Undichtigkeit in derselben ;-)
Wie immer halt. ;-)
Da sich marcel leider nicht mehr meldet, werden wir wohl kein
kompilierbares Beispiel mehr erhalten... :-(