Forum: Compiler & IDEs Merkwürdiges Verhalten bei Variablen und for-schleifen


von Daniel G. (motello)


Lesenswert?

Hallo,

ich arbeite an einem Projekt für einen ARM9 mit gcc. Es treten 
merkwürdige Phänomene auf:

1. Globale Variablen lassen sich nicht mit 0 initialisieren, sie 
enthalten stattdessen einen undefinierten Wert. Andere Werte gehen aber.

2. Eine For-Schleife zählt manchmal bis ins Unendliche. Wenn ich eine 
Code-Zeile davor setze, egal was, geht es teilweise. Hier ein Beispiel:
1
unsigned short ip_chksum (int header_length)
2
{
3
    unsigned short u16;
4
    unsigned long sum = 0;
5
    int i;
6
7
    for (i = 0; i < 10; i++) {
8
        sum += endians(ip.shorts[i]);
9
    }
10
11
    while (sum >> 16)
12
        sum = (sum & 0xFFFF) + (sum >> 16);
13
14
    return (unsigned short)~sum;
15
}
Habe diese Funktion schon mehrfach umgeschrieben und immer wieder bleibt 
das Programm darin hängen. In der Ursprünglichen Variante dieser 
Funktion funktionierte sie nur, wenn ich das IP-Checksum Feld des 
IP-Header-Structs nicht vor der Funktion auf null setzte. Somit wurde 
zwar eine falsche Checksumme berechnet, aber immerhin lief das Programm 
weiter.

Das umfangreiche Programm funktioniert sonst gut, nur manchmal stolpere 
ich über die genannten Probleme. Ich hoffe jemand hat einen Tipp für 
mich.

Viele Grüße
Daniel

von Karl H. (kbuchegg)


Lesenswert?

Probleme der genannten Art sind des öfteren Folgen von Out-Of-Bounds 
Array Zugriffen, also Fehlern in deinem Code

zb
1
int i;
2
int j[5];
3
int k;
4
5
int main()
6
{
7
  i = 4;
8
  k = 5;
9
  j[5] = 8;
10
}

Hier wird auf j out-of-bounds zugegriffen. j ist ein Array mit 5 
Elementen, durchnummeriert von 0 bis 4. Ein Element 5 existiert schlicht 
und ergreifend nicht. Wird es im obigen Code trotzdem beschrieben, 
ändert entweder i oder k (je nachdem wie sie der Compiler im Speicher 
angeordnet hat) scheinbar unmotiviert seinen Wert.

Nun ist obiger Fall trivial zu detektieren. Interessanter wird es, wenn 
derartige Fehler bei funktionslokal angelegten Arrays passieren. Dann 
kann manchmal alles mögliche passieren, weil man sich unter Umständen 
den Return-Stack komplett zerschiesst.

Eine andere Variante (oder eigentlich dasselbe Problem nur anders 
verpackt) ist ein Zugriff über Pointer die 'in den Wald zeigen'.

Kurz und gut: Solche Phenomäne wie du sie bechreibst sind meistens ein 
Hinweis darauf, dass man Speicher beschreibt, an denen man nichts zu 
suchen hat.

Viel Spass beim Suchen. Solche Probleme sind schwer zu finden.
Erster Schritt: reproduzierbar machen. Die Aussage 'manchmal passiert 
dieses' muss ersetzt werden durch 'wenn ich zuerst A mache, dann B, dann 
C, dann passiert dieses und jenes'

von Daniel G. (motello)


Lesenswert?

Hi,

ich beobachte diese Phänomene seit ich vor Monaten mit dem Projekt 
begonnen habe. Leider konnte ich trotz vielen rumprobierens bisher 
nichts eingrenzen. Das IP-Feld besteht aus 20 bytes, also 10 
16-bit-worten. Die angegebene For-Schleife sollte also nicht über das 
Array hinaus laufen.

Ich habe schon etwas programmiererfahrung in C, kenne die Probleme die 
du beschrieben hast und bin daher darauf sensibilisiert. Im gesamten 
Programm wird ständig mit Pointern hantiert und empfangene 
Netzwerkheader werden auseinandergenommen um an gewünschte Werte zu 
kommen. Alles funktioniert sonst.

Aber warum werden dann globale Variablen nicht richtig mit 0 
initialisiert? Folgender Code würde ein Error liefern:
1
int i = 0;
2
int test () {
3
    if(i != 0)
4
        return error;
5
}
Eine 1 statt einer 0 geht wiederrum. Dabei wäre ja nicht mal das '=0' 
nötig bei globalen Variablen, wie ich im Forum gelesen habe.

Vielleicht gibt es noch andere Ursachen für so ein Problem. Wenn nicht 
muss ich eben weiter und weiter suchen...

von (prx) A. K. (prx)


Lesenswert?

Mögliche Gründe gibt es im Dutzend. Stacküberlauf ist einer davon.

Wenn du mal einen Code beisammen hast, bei dem du genau identifizieren 
kannst welche Variable offensichtlich irgendwann versehentlich 
überschrieben wird, dann hilft dir ein Data Watchpoint im Debugger.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Daniel G. schrieb:

> 1. Globale Variablen lassen sich nicht mit 0 initialisieren, sie
> enthalten stattdessen einen undefinierten Wert. Andere Werte gehen aber.

Klingt nach einem Problem mit dem Startup-Code deiner Bibliothek
(oder wo auch immer er her kommt -- ich kenne mich mit ARMs nicht
so aus).

Eine nicht initialisierte globale (oder statische) Variable wird
implizit mit 0 initialisiert.  Dies erfolgt, indem der Compiler
sie in eine section namens .bss steckt.  Alle diese Variablen werden
vom Linker dann zusammengefasst, und es ist die Verantwortung des
Startup-Codes, den gesamten BSS-Bereich beim Start auszunullen.

Eine initialisierte globale Variable landet in der section .data.
Der Startup-Code muss beim Start hier die Initialwerte aus externem
Speicher (Festplatte, ROM, ...) laden.

Eine globale Variable, die überflüssigerweise mit 0 initialisiert
wird, wird vom Compiler (als kleine Optimierung) ebenfalls nach .bss
gesteckt.

von Daniel G. (motello)


Lesenswert?

Der Stack Pointer liegt etwa 62MB hinter dem letzten Image-Byte. Der 
läuft nicht so schnell über. Zudem benutzt die Funktion den Stack gar 
nicht, wie ich eben aus dem Assembler Code entnommen habe.

Was den Startup Code angeht: ich werde jetzt erstmal meine Toolchain neu 
installieren und dabei alle Optionen überprüfen. vielleicht ist etwas 
nicht richtig eingestellt oder installiert.

von Karl H. (kbuchegg)


Lesenswert?

Daniel G. schrieb:
> Der Stack Pointer liegt etwa 62MB hinter dem letzten Image-Byte. Der
> läuft nicht so schnell über. Zudem benutzt die Funktion den Stack gar
> nicht, wie ich eben aus dem Assembler Code entnommen habe.

Du solltest auch berücksichtigen, dass du nur die Symptome siehst und 
nicht die Ursache. Die Ursache muss nicht in der Funktion zu finden 
sein, die Probleme macht. Eigentlich ist die Ursache für solche Probleme 
meistens ganz weit von der Stelle entfernt an der man auf das Problem 
aufmerksam wird. Auch deshalb sind solche Probleme oft so schwer zu 
identifizieren.

von (prx) A. K. (prx)


Lesenswert?

Es gibt auf ARMs mehrere Stacks. Einen für das Hauptprogramm, einen für 
normale Interrupts, usw. Je nachdem wie die konfiguriert werden, können 
die also auch bei massig Speicher schnell ineinander laufen.

Wenn beispielsweise der IRQ-Stack direkt hinter dem normalen Stack liegt 
und zu klein konfiguriert ist, dann plättet dir jeder Interrupt die 
lokalen Daten der obersten Level des Hauptprogramms weg. Allerdings auch 
die Return-Adressen, weshalb man das meistens daran merkt, dass er bei 
Return über die Wupper geht.

Für sowas hilft eine Analyse des Mapfiles in Verbindung mit dem 
Verständnis des Startup-Codes.

von tuppes (Gast)


Lesenswert?

Eins kann man aber wohl in Stein meißeln: Es ist (gerade beim gcc) mit 
ganz ganz großer Wahrscheinlichkeit kein Compiler-Fehler oder etwas 
ähnliches. Umformulieren von Code wird das Problem daher nur 
verschleiern.

Ich würde folgendes versuchen:

1. Am Anfang von main() müssen alle globalen Daten korrekt initialisiert 
sein. Die, die nicht explizit initialisiert wurden, müssen auf 0 stehen. 
Das zuerst im Debugger prüfen. Wenn nicht, dann ist der Startupcode 
faul. Woher stammt der überhaupt?

2. Wenn OK, dann Schritt für Schritt weitergehen und darauf achten, 
durch welche Aktion sich die globalen Daten unerwartet ändern. Diesen 
Punkt möglichst eng eingrenzen.

3. In was für einem Speicher liegen die fraglichen Daten? Internes RAM 
oder externer Speicher, für den ein Businterface konfiguriert werden 
muss? Wenns extern ist, ist das Interface evtl. falsch aufgesetzt und 
liefert Unsinn.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

tuppes schrieb:
> Eins kann man aber wohl in Stein meißeln: Es ist (gerade beim gcc) mit
> ganz ganz großer Wahrscheinlichkeit kein Compiler-Fehler oder etwas
> ähnliches.

Mal abgesehen davon, dass man mit der Fehlerbeschreibung kaum erraten 
kann, wo der Fehler liegen könnte (bedeutet "immer wieder" das gleiche 
wie "ausnahmslos, jedes Mal"?), entbehrt die Betonung auf "gerade beim 
gcc" nicht einer gewissen Ironie :-)

Wenn man den Fehler mit der vom OP genannten Funktion zuverlässig 
reproduzieren kann, dann würde sich ein Disassembly durchaus lohnen.

Gruß
Marcus
http://www.doulos.com/arm/

von Daniel G. (motello)


Lesenswert?

Vielen Dank für die vielen Antworten!

Die Antwort von A.K. kam gerade noch rechtzeitig um mich vor einer 
Neuinstallation der Toolchain zu bewahren. :-)

Folgende Dinge sind auf jeden Fall schief gelaufen:

1. Ich dachte den Code für die Variableninitialisierung gegeriert der 
Compiler von sich aus. Somit hatte ich in meinem (selbstgeschriebenen) 
Startupcode keinen derartigen Code.

2. Der Hinweis, dass ARMs mehrere Stacks haben, war ein Treffer! Ich 
habe gar nicht daran gedacht auch die Stackpointer der anderen 
Prozessormodi zu initialisieren.

Wenn ich jetzt so drüber nachdenke, passen diese Versäumnisse genau auf 
die aufgetretenen Verhaltensweisen des Programms.

Ich werde nun entweder meinen Startupcode erweitern oder einen fertigen 
nehmen. Libgloss soll, wie ich gestern gelesen habe, Startupcode für 
verschiedene Targets beinhalten.

Wenn ich Resultate habe melde ich mich wieder. Viele Grüße,
Daniel

von Daniel G. (motello)


Lesenswert?

So, habe nun Folgendes in meinen Startup Code aufgenommen:

1. .bss und COMMON Section wird mit Nullen überschrieben

2. Stackpointer werden korrekt initialisiert

Bisher traten keine weiteren Probleme (der beschiebenen Art) auf. Danke 
nochmal für eure Hilfe!

Ich habe aber noch Fragen:

1. Kann es sein, dass die Option -nostartfiles für den Linker 
verhindert, dass der Compiler eine C-Runtime mit einbringt?

2. Wieso liegen meine globalen uninitialisierten Variablen in COMMON und 
nicht in .bss?

3. Wenn ich ein Struct einer Funktion übergebe, wird es dann wirklich im 
Stack übergeben? Wenn das Struct sehr groß ist, dann ist der Stack ja 
schnell voll, meiner beispielsweise hat eine Größe von 4kB. Laut C-Buch 
soll es so sein, und ich nutze daher nur Pointer auf Structs als 
Parameter.

Viele Grüße
Daniel

von (prx) A. K. (prx)


Lesenswert?

Daniel G. schrieb:

> 1. Kann es sein, dass die Option -nostartfiles für den Linker
> verhindert, dass der Compiler eine C-Runtime mit einbringt?

Ja.

> 3. Wenn ich ein Struct einer Funktion übergebe, wird es dann wirklich im
> Stack übergeben? Wenn das Struct sehr groß ist, dann ist der Stack ja
> schnell voll, meiner beispielsweise hat eine Größe von 4kB.

Weshalb man komplette structs auch nur selten als Wert übergibt, 
allenfalls wenn sehr klein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Daniel G. schrieb:

> 2. Wieso liegen meine globalen uninitialisierten Variablen in COMMON und
> nicht in .bss?

Historisches Unix-C-Compiler-Verhalten.  Damit musste man eine
Deklaration einer nicht initialisierten globalen Variablen nicht
von einer Definition unterscheiden, sondern kann bspw. in einer
Headerdatei stehen haben:
1
int someglobal;

Alle Quelldateien, die sich das reinziehen, legen dann eine identische
Deklaration dafür im Objektcode ab, die als COMMON deklariert ist.
Wer noch FORTRAN lernen durfte weiß, wofür der Linker sowas mal können
musste. :-)  Der Linker überlagert nämlich all diese COMMON-Blöcke,
sodass gleichartige Definitionen auch wirklich auf das gleiche Objekt
zeigen.

Dieses Verhalten ist vom C-Standard explizit gestattet und als eine
von zwei (glaub' ich) möglichen Alternativen spezifiziert.

Mittels -fno-common schaltet man beim GCC auf die andere Alternative
um.

Normalerweise sollte der Linkerscript die COMMON-Dinger auflösen und
dann mit .bss zusammenführen.

von Daniel G. (motello)


Lesenswert?

Zuvor hatte ich Folgendes im Linker Script stehen:

    /* collect all uninitialized .bss sections */
    .bss (NOLOAD) : {
        . = ALIGN(4);
        _sbss = .;
        *(.bss)
        _ebss = .;
    }>ram

Damit lagen alle COMMON Objekte hinter .bss und eben nicht zwischen 
_sbss und _ebss (laut map file).

Nach hinzufügen von *(COMMON) hinter *(.bss) wurden dann alle COMMON 
Objekte in die Section .bss aufgenommen.

von Daniel G. (motello)


Lesenswert?

Leider muss ich den Thread fortsetzen: Zwar konnte ich die Probleme 
größtenteils lösen aber ein weiteres und ähnliches Problem tauchte auf.

Ich habe mehrere verschachtelte if-Abfragen. Je nachdem ob ich eine 
Codezeile vor einer bestimmten if-Abfrage einfüge, wird die nachfolgende 
if-Abfrage korrekt behandelt oder (beides, if und else) einfach 
übersprungen.

1. Das dürfte wohl auch auf Speichersalat zurückzuführen sein, aber ich 
weiß nicht wo ich noch suchen soll oder wo zuerst. Die beiden 
Stackpointer (SVC und IRQ) sind initialisiert, andere Prozessormodi 
nutzt das Programm nicht. Die Fehler tritt im IRQ Modus auf.

2. Die Stacks sind riesig und ich habe sie testweise noch riesiger 
gemacht. Zudem übergeben ich höchstens 32Bit Variablen an Funktionen. 
Also Stacküberlauf schließe ich aus.

3. Natürlich habe ich vielfach den C-Code auf fehlerhafte Pointer, 
Kopierroutinen etc. durchgesehen und verschiedenes testweise 
auskommentiert.

Ich komme langsam in Zeitnot und bin über jeden Hinweis dankbar, der 
mich den Fehler schneller finden lässt!

Ich hatte noch zwei Vermutungen:
V1. Linker Script nicht korrekt. Dazu habe ich mir das ld manual 
durchgelesen und das Linkerscript nach meinem ermessen umgeschrieben. Es 
hat sich nichts verändert.
V2. Irgendein Problem mit THUMB und ARM Code bzw. Interworking. Kann das 
sein?

Grüße,
Daniel

von Daniel G. (motello)


Lesenswert?

Ich habe gerade noch rausgefunden, dass je nach Optimierungseinstellung 
des Compilers sich das Verhalten des Codes ändert. Vielleicht deutet das 
ja auf etwas hin?

Danke und Gruß,
Daniel

von Stefan E. (sternst)


Lesenswert?

Könnte einfach nur ein fehlendes "volatile" sein.

von Daniel G. (motello)


Lesenswert?

Das habe ich aus anderen Gründen schon mehrmals überprüft. Alle von 
Exceptions und Task-Code gemeinsam verwendeten Variablen sind als 
volatile gekennzeichnet.

Aber dieses shared data Problem kann doch nicht solch fehlerhaftes 
Verhalten herbeirufen!?

von Daniel G. (motello)


Lesenswert?

Vielleicht fehlt ja noch was in meinem Startup File. Ich lese immer 
wieder, dass initialisierte Variablen geladen oder kopiert werden 
müssen. Entsprechend meines Linker Scripts sollten die aber bereits im 
Image liegen:

    .data : {
        _sdata = .;
        *(.vectors)
        *(.data)
        _edata = .;
    } > ram

Oder habe ich was falsch verstanden?

Nebenbei, weiss jemand, was .vectors zu bedeuten hat?

von Stefan E. (sternst)


Lesenswert?

Daniel G. schrieb:

> Aber dieses shared data Problem kann doch nicht solch fehlerhaftes
> Verhalten herbeirufen!?

Klar. Wenn du z.B. in dem if eine Variable abfragst, die eigentlich 
volatile sein müsste, dann kann zusätzlicher Code vor diesem if dazu 
führen, dass die Variable nicht mehr dauerhaft in Registern gehalten 
werden kann, und somit beim if neu aus dem Speicher gelesen werden muss.

von Daniel G. (motello)


Lesenswert?

Habs nochmal überprüft, alles volatile was benutzt wird.

Aber das Problem ist ja folgendes:
1
if (a)
2
    func1();
3
else
4
    func2();

Weder func1() noch func2() wird ausgeführt, solange vor dem if nicht 
eine zusätzliche Code-Zeile eingefügt ist.

von Daniel G. (motello)


Lesenswert?

Ich habe noch etwas rausgefunden:

Der Fehler tritt auf, egal an welcher Stelle die Funktion im Speicher 
liegt. Es muss also etwas mit der Funktion an sich zu tun haben.

Ich habe langsam den Verdacht, dass mit lokalen Variablen nicht richtig 
umgegangen wird. Muss ich im Linker Script irgendetwas beachten, dass 
lokalen Variablen auch Platz eingeräumt wird?

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Daniel G. schrieb:
> Ich habe gerade noch rausgefunden, dass je nach Optimierungseinstellung
> des Compilers sich das Verhalten des Codes ändert. Vielleicht deutet das
> ja auf etwas hin?

Würdest Du uns auch verraten, wie sich das Verhalten ändert? 
Funktioniert das Programm wenn Du optimierst, oder wenn Du nicht 
optimierst? Im letzteren Fall, könnte das auf weitere Probleme mit dem 
Stack hinweisen.

Bei geringer Optimierung scheint GCC keine Register für lokale Variablen 
zu verwenden, sondern nur den Stack. Wenn der nicht richtig aufgesetzt 
wurde, dann knallts. Sobald bei höheren Optimierungsstufen für lokale 
Variablen Register verwendet werden, kann das Problem verschwinden.

Ich denke an weitere Stackprobleme, da Du erwähntest, dass nur SVC und 
IRQ stack verwendet werden. Läuft Dein Programm nur im SVC mode oder 
wird irgendwann auch mal in den USR/SYS mode umgeschaltet? Dann musst Du 
dessen Stack natürlich auch noch initialisieren.

Gruß
Marcus
http://www.doulos.com/arm/

von Daniel G. (motello)


Lesenswert?

Hi,

habs vergessen dazu zu schreiben: in allen Optimierungsroutinen läuft 
der Code fehlerhaft, nur eben unterschiedlich fehlerhaft. Ohne 
Optimierung geht garnichts mehr.

Ich habe die Initialisierung bereits für alle anderen Modi in meinen 
Startup Code übernommen. Nun Arbeitet der Prozessor im User-Mode und 
IRQ.

Wenn ich den Stack verschiebe oder die Anordnung der Unterprogramme im 
Speicherimage, ändert sich nichts, gleiche Fehler.

Reicht es, wenn ich die Stackpointer initialisiere oder muss ich mehr 
machen? Habe mal was gelesen, dass der Stackbereich mit Nullen 
überschrieben wird. Halte das bisher für unnötig.

Gruß,
Daniel

von Daniel G. (motello)


Lesenswert?

Ich bin leider immer noch nicht weiter und hoffe auf Hilfe! Vielleicht 
kann sich ja jemand meine c-runtime ansehen ob dort ein Fehler ist. Ich 
wäre sehr dankbar!

Der Vectortable liegt nicht am Anfang des Speichers. Start ist .start 
von wo aus der table dann kopiert wird und den Vectortable der Bootstrap 
routine überschreibt. Das funktioniert immerhin.

Wie gesagt: unterschiedliches fehlerhaftes Verhalten je nach 
Optimierungsgrad. Ohne Optimierung geht nichts mehr, da wird nicht mal 
die erste Zeile der main-funktion ausgeführt.

Viele Grüße
Daniel
1
/************************************************
2
3
 * DEFINITIONS                                  *
4
5
 ************************************************/
6
7
8
9
#define STACK_SIZE   0x1000
10
11
12
13
#define ARM_MODE_USER    0b10000
14
15
#define ARM_MODE_ABORT   0b10111
16
17
#define ARM_MODE_FIQ     0b10001
18
19
#define ARM_MODE_IRQ     0b10010
20
21
#define ARM_MODE_SVC     0b10011
22
23
#define ARM_MODE_SYSTEM  0b11111
24
25
#define ARM_MODE_UDEF    0b11011
26
27
28
29
#define I_BIT            0x80
30
31
#define F_BIT            0x40
32
33
34
35
/************************************************
36
37
 * VECTOR TABLE                                 *
38
39
 ************************************************/
40
41
42
43
  .section ".vectortable"
44
45
46
47
/* Exception vectors */
48
49
  b   reset_vector    /* reset */
50
51
  b   undef_vector   /* Undefined Instruction */
52
53
  b   swi_vector     /* Software Interrupt */
54
55
  b   pabt_vector    /* Prefetch Abort */
56
57
  b   dabt_vector    /* Data Abort */
58
59
  .word  _edata    /* Size of the image for SAM-BA */
60
61
  ldr pc, [pc, #-0xf20]   /* IRQ : read the AIC */
62
63
  b   fiq_vector      /* FIQ */
64
65
66
67
undef_vector:
68
69
  b   undef_vector
70
71
swi_vector:
72
73
  b   swi_vector
74
75
pabt_vector:
76
77
  b   pabt_vector
78
79
dabt_vector:
80
81
  b   dabt_vector
82
83
rsvd_vector:
84
85
  b   rsvd_vector
86
87
irq_vector:
88
89
    ldr     pc, [pc, #-0xf20]
90
91
fiq_vector:
92
93
  b   fiq_vector
94
95
reset_vector:
96
97
  b  reset_vector
98
99
100
101
102
103
/************************************************
104
105
 * INITIALIZE STACK POINTER                     *
106
107
 ************************************************/
108
109
110
111
  .text
112
113
  .globl start
114
115
116
117
start:
118
119
120
121
/* SUPERVISOR Mode */
122
123
  ldr     sp,=TOP_OF_MEM
124
125
126
127
/* IRQ Mode */
128
129
  sub     r0, sp, #STACK_SIZE
130
131
  mov     r1, #ARM_MODE_IRQ | I_BIT | F_BIT
132
133
  msr     cpsr_c, r1
134
135
  mov     sp, r0
136
137
138
139
/* FIQ Mode */
140
141
  sub     r0, sp, #STACK_SIZE
142
143
  mov     r1, #ARM_MODE_FIQ | I_BIT | F_BIT
144
145
  msr     cpsr_c, r1
146
147
  mov     sp, r0
148
149
150
151
/* ABORT Mode */
152
153
  sub     r0, sp, #STACK_SIZE
154
155
  mov     r1, #ARM_MODE_ABORT | I_BIT | F_BIT
156
157
  msr     cpsr_c, r1
158
159
  mov     sp, r0
160
161
162
163
/* UDEF Mode */
164
165
  sub     r0, sp, #STACK_SIZE
166
167
  mov     r1, #ARM_MODE_UDEF | I_BIT | F_BIT
168
169
  msr     cpsr_c, r1
170
171
  mov     sp, r0
172
173
174
175
/* USER/SYSTEM Mode, enable IRQ */
176
177
  sub     r0, sp, #STACK_SIZE
178
179
  mov     r1, #ARM_MODE_USER | F_BIT
180
181
  msr     cpsr_c, r1
182
183
  mov     sp, r0
184
185
186
187
/************************************************
188
189
 * COPY VECTOR TABLE                            *
190
191
 ************************************************/
192
193
194
195
    ldr     r0, =_svec
196
197
    ldr     r1, =_evec
198
199
    mov     r2, #0    /* destination for the vector table */
200
201
1:
202
203
    cmp     r0, r1
204
205
    ldrcc   r3, [r0], #4
206
207
    strcc   r3, [r2], #4
208
209
    bcc     1b
210
211
212
213
/************************************************
214
215
 * zero the .bss section                        *
216
217
 ************************************************/
218
219
220
221
        ldr     r1, =_sbss
222
223
        ldr     r2, =_ebss
224
225
        mov     r0, #0
226
227
1:
228
229
        cmp     r1, r2
230
231
        strcc   r0, [r1], #4
232
233
        bcc     1b
234
235
236
237
/************************************************
238
239
 * BRANCH TO MAIN                               *
240
241
 ************************************************/
242
243
244
245
/* branch to main funktion */
246
247
  b       main

von Peter (Gast)


Lesenswert?

Ich habe einen Thread hier gepostet mit dem gleichen Problem. Hast du es 
gelöst?

von (prx) A. K. (prx)


Lesenswert?

Der gezeigte Startupcode jedefalls vergisst, die initialisierten Daten 
zu initialisieren (aus dem Flash zu kopieren).

von Daniel G. (motello)


Lesenswert?

Nein, es ist leider nicht gelöst. Ich habe daher kürzlich einen neuen 
Thread gestartet "Ohne Optimierung gehts nichts mehr" oder so ähnlich. 
Aber auch dort leider kein Ergebnis.

Das Image mit allen Daten wird durch AT91Bootstrap in RAM kopiert.

Grüße
Daniel

von Daniel G. (motello)


Lesenswert?

Ich habe das Problem möglicherweise gelöst:

Ich habe mir die Stackpointer Initialisierung von der Atmel 
at91bootstrap Routine abgeschaut, wo der benutzte Stackpointer auf 
0x23ffffff, also Ende des Speichers gesetzt wird.

Ist das OK? Es ist ein full stack und somit wird der erste Wert 
unaligned mit drei bytes im Nirgendwo gespeichert. Wie verhält sich STM 
bei unaligned data? Wird überhaupt etwas gespeichert? Und wieso macht 
Atmel sowas?

Grüße,
Daniel Gering

von (prx) A. K. (prx)


Lesenswert?

"Full" heisst, dass SP auf das unterste belegte Byte zeigt, also erst 
dekrementiert und dann gespeichert wird. Daher wäre 0x24000000 kein 
Problem, sondern vielmehr der klassische Anfangswert eines 
Stackpointers.

Aber 0x23ffffff für einen Stackpointer wäre wirklich äusserst seltsam, 
denn das wäre eben unaligned und sowas ist bei Stacks ziemlich verboten, 
selbst wenn eine Maschine sowas erlauben sollte, was beim ARM m.W. nicht 
der Fall ist. Allerdings meckert ARM zumindest in Standardeinstellung 
wohl auch nicht, was zu allerlei Unfug führen kann.

Wenn das tatsächlich der Fall ist, dass besteht möglicherweise ein 
Missverständnis zwischen Startup-Code und den Werten/Namen vom 
Linker-Script.

von Daniel G. (motello)


Lesenswert?

Oh ja, bei full stack hab ich mich geirrt. Dann liegt es wohl nur am 
alignment. Und es war auch vorschnell und falsch zu sagen, dass Atmel es 
unaligned gemacht hat. Das muss ich wohl wo anders her oder selbst 
falsch gemacht haben.

Ich nehme an, dass mit steigender Optimierung der Stack weniger benutzt 
wird und das Programm somit immer besser lief.

Compiler (codesourcery) hat übrigens nicht gewarnt bei -Wall.

Danke A.K., ihre kompetenten Beiträge sind wirklich ein Gewinn für 
dieses Forum!

Dann kann ich mich ja jetzt endlich wieder um die eigentliche 
Programmentwicklung kümmern.

Viele Grüße
Daniel Gering

von (prx) A. K. (prx)


Lesenswert?

Daniel G. schrieb:

> Compiler (codesourcery) hat übrigens nicht gewarnt bei -Wall.

Der Stackpointer wird in deinem handgebauten Startupcode definiert und 
aus einer externen Adresse abgeleitet, die wohl dem Linker-Script 
entstammt.

Also woher sollte der Compiler die exakte Adresse des Stacks kennen?

von Daniel G. (motello)


Lesenswert?

Dem Compiler wird die Adresse über die Option -DTOP_OF_MEMORY 
mitgeteilt. TOP_OF_MEMORY ist dabei eine im Makefile initialisierte 
Variable.

von (prx) A. K. (prx)


Lesenswert?

Die wird auch in dieser Form nicht dem Compiler mitgeteilt, sondern dem 
Präprozessor vor dem Assembler, der deinen Startup-Code verarbeitet. 
Assemblerprogrammierung heisst bekanntlich Schrott-rein-Schrott-raus. 
Sorry, aber du kannst nicht den Compiler für deinen Fehler in Haftung 
nehmen, der hat von solchen absoluten Adressen keine Ahnung.

von Daniel G. (motello)


Lesenswert?

> Allerdings meckert ARM zumindest in Standardeinstellung
> wohl auch nicht, was zu allerlei Unfug führen kann.

Ich habe den Satz wohl falsch verstanden, ich dachte du meintest den 
Compiler und dessen Warnungen. Natürlich ist es schön wenn ein Compiler 
bei Bugs warnt, aber ich geben selbstverständlich nicht dem Compiler 
schuld wenn ich einen Fehler mache und er nicht warnt.

Was meinst du mit dem obigen Satz? Wie soll er meckern? Exception?

Also bekommt der Compiler nichts weiter als eine Referenz auf die 
Speicheradresse in der der TOP_OF_MEM Wert steht?

von (prx) A. K. (prx)


Lesenswert?

Daniel G. schrieb:

> Was meinst du mit dem obigen Satz? Wie soll er meckern? Exception?

Es gibt Prozessoren mit unaligned trap. In ARMv6 (ARM11) besteht 
offenbar die Möglichkeit, solche Zugriffe entweder einen Trap laufen zu 
lassen, oder korrekt auszuführen.

> Also bekommt der Compiler nichts weiter als eine Referenz auf die
> Speicheradresse in der der TOP_OF_MEM Wert steht?

Der Compiler kriegt davon garnichts mit. Der verlässt sich darauf, dass
am Anfang einer Funktion der SP einen sinnvollen Wert enthält und sorgt 
dafür, dass was ihn angeht aus einem korrekten SP kein unkorrekter wird. 
Optional wird Code eingebaut, der auf Stacküberlauf testet. Das wär's 
dann aber auch. Den absoluten Wert kennt er nicht. Der 
Präprozessor-Makro TOP_OF_MEM ist für den Compiler völlig uninteressant.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Daniel G. schrieb:
> Ich nehme an, dass mit steigender Optimierung der Stack weniger benutzt
> wird und das Programm somit immer besser lief.

Sach' ich ja.

A. K. schrieb:
> Es gibt Prozessoren mit unaligned trap. In ARMv6 (ARM11) besteht
> offenbar die Möglichkeit, solche Zugriffe entweder einen Trap laufen zu
> lassen, oder korrekt auszuführen.

Geht auch bei einigen ARM9E basierten Systemen. Siehe z.B. 
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0198e/I1039296.html
Allerdings bieten ARMv6/7 noch mehr Konfigurationsmöglichkeiten.

Gruß
Marcus
http://www.doulos.com/arm/

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.