Mir ist nicht klar, warum die Long Long Variante einen unaligned access
auslöst und die Int-Variante nicht. Der pointer unaligned liegt doch in
beiden Fällen auf einer nicht durch 4 teilbaren Adresse, oder nicht?
Long Long Variante:
===================
Decius schrieb:> Mir ist nicht klar, warum die Long Long Variante einen unaligned access> auslöst und die Int-Variante nicht.
Der erzeugte Assemblercode kann darüber vermutlich Auskunft geben.
Einen int32_t kann man immer in einem int64_t verstecken, d.h. der
Compiler kann theoretisch ein einzelnes LDRD auf eine ausgerichtete
Adresse durchführen, maskieren und shiften. Mit einem int64_t geht das
nicht mehr.
@ S.R
Kann gut sein das es an einer prinzipiell anderen Umsetzung auf
Assemblerebene liegt.
Die URL weiss ich nicht mehr, aber ich hatte gelesen, dass der Compiler
aufgrund der Effizienz versucht bei aligned Zugriffen zu bleiben. Könnte
also sein das er in der Int-Variante den unaligned Zugriff dadurch
verhindert, dass er ihn mit aligned Zugriffen nachbildet. Was dann aber
wahrscheinlich bei der Long Long Variante mit vertretbarer Effizienz
nicht mehr möglich ist.
Das ist jetzt nur eine Vermutung meinerseits, es würde den Effekt aber
erklären.
Decius schrieb:> Kann gut sein das es an einer prinzipiell anderen Umsetzung auf> Assemblerebene liegt.
Das ist sogar äußerst wahrscheinlich. Guck dir den Code an, dann hast du
den exakten Grund. Rumtheoretisieren bringt im konkreten Fall nix.
Je nach Architektur sind unaligned-Zugriffe einfach nur ein
Performance-Problem (x86), verboten (manche ARMs), oder produzieren
Datenmüll (andere ARMs, m68k). Dementsprechend machen die Compiler auch
unterschiedliche Dinge.
Der imx6q hat einen armv7-a Core. Damit unterstützt dieser sogar
unaligned Zugriffe in Hardware, wenn diese korrekt initialisiert wird.
Es ist zwar nicht empfohlen, aber es sollte schon möglich sein mit dem
GCC, diesem uC und unaligned Zugriffen zu arbeiten. Ich werde durch ein
fremdes Gerät gezwungen, damit umgehen zu können.
Ansonsten danke für Deine Antworten S.R.
Genaueres kann ich nicht vollständig schreiben, da ich an dem Folgenden
noch arbeite, und so das Thema noch nicht gelöst ist. Aber der unaligned
access wird wahrscheinlich die Hardwareintialisierung, die
Linuxkernelübersetzung, das Bauen der Linuxdistribution und die
Applikationscompilation tangieren. Die Applikation am Anfang dieses
Threads, noch etwas erweitert, dient dazu meine Linuxdistribution zu
testen, ob sie mit unaligned Zugriffen umgehen kann.
1) Hardware:
MRC p15,0,r4,c1,c0,0 /* read SCTL to r4 */
ORR r4,r4,#0x00400000 /* enable unaligned mode (U=1) */
BIC r4,r4,#0x00000002 /* disable alignment faults (A=0) */
MCR p15, 0, r4, c1, c0, 0 /* write r4 to SCTL*/
2) Linuxkernel:
muss mit den folgenden gcc optionen übersetzt werden.
-march=armv7-a
-mtune=cortex-a9
-munaligned-access
3) Linuxdistribution:
Aufgrund des neuen Kernels muss die Linuxdistribution neu gebaut werden.
4) Applikation:
muss mit den folgenden gcc optionen übersetzt werden.
-march=armv7-a
-mtune=cortex-a9
-munaligned-access
Punkt 1 erfolgt korrekt, das wurde durch Auslesen des STCL Registers
innerhalb eines Treibers getestet. Thema im Moment sind die Punkt 2 und
3.
Wie genau diese beiden Punkte erfolgen müssen, hängt natürlich vom
Buildsystem ab. Aber habe ich da eventuell vom Prinzip her noch etwas
vergessen?
Decius schrieb:> Der imx6q hat einen armv7-a Core. Damit unterstützt dieser sogar> unaligned Zugriffe in Hardware, wenn diese korrekt initialisiert wird.
Aber nur bei max. 4-Byte-Zugriffen! Die LDRD/STRD Instruktionen, welche
vermutlich für deine 8-Byte-Variable genutzt werden, brauchen immer
Word-Alignment bei der Adresse (siehe Kapitel "A3.2.1
Unaligned data access" im ARMv7A/R Architecture Reference Manual).
Solche Probleme kann man grundsätzlich vermeiden, indem man keine
Pointer wild umcastet und dereferenziert. Das ist in C(++) nämlich
undefiniertes Verhalten (bis auf bestimmte Ausnahmen). Für fast alle
solche Fälle gibt es korrekte wohldefinierte Vorgehensweisen, die
garantiert immer auf jeder Plattform korrekt funktionieren. Weil du
nicht geschrieben hast was du eigentlich vorhast, kann man hier aber
keinen Lösungsvorschlag machen.
A. K. schrieb im Beitrag #5041921:
> malloc() solle eigentlich Pointer zurückgeben, die zum Alignment jedes> beliebigen Datentyps passen.
Ja, aber nur der Original-Pointer, nicht das Ergebnis von
Pointer-Arithmetik...
Decius schrieb:> Der imx6q hat einen armv7-a Core. Damit unterstützt dieser sogar> unaligned Zugriffe in Hardware, wenn diese korrekt initialisiert wird.
Nicht für 64 Bit-Zugriffe, weil diese - wenn unaligned - nicht atomisch
sind.
Decius schrieb:> Ich werde durch ein fremdes Gerät gezwungen, damit umgehen zu können.
Das hätte ich gerne etwas erklärt. ;-)
Hast du einen Linux-Treiber (bzw. -Anwendung), die böses Alignment
macht?
Welchen Compiler und Version verwendest Du?
Ich kenne den imx6q nicht, auf einem RISC können solche casts zu
unschönen core dumps führen. Je nach Compiler wird man gewarnt.
Auf einem Intel x86 ist das z.B. kein Problem.
Der Grund dafür ist, wenn der Speicher an einer Page Grenze liegt.
Malloc interessieren die Page Grenzen nicht.
C11 kennt dafür aligned_malloc(). Evtl. kennt dein Compiler auch
posix_memalign().
Grüsse,
René
Als Buildsystem wird das yocto 1.8.2 verwendet. Das oben ist nur ein
Beispielprogramm aus dem Internet um sicher einen Unaligned access
provozieren zu können, es hat überhaupt nichts mit dem eigentlichen
Programm zu tun. Aber ok da wäre dann wohl eine Lösung ohne Long Long
Variablen besser.
Decius schrieb:> Aber ok da wäre dann wohl eine Lösung ohne Long Long Variablen besser.
Und sobald jemand den unaligned Access ganz abschaltet, oder den Treiber
auf eine andere Plattform portiert, geht es wieder kaputt? Wäre es nicht
besser, ganz auf undefinierte casts zu verzichten und es korrekt zu
implementieren? Das geht mit großer Wahrscheinlichkeit auch mit 64 Bit
Typen, man muss nur wissen was du eigentlich bezweckst.
@Dr. Sommer
Das oben ist nur ein
Beispielprogramm aus dem Internet um sicher einen Unaligned access
provozieren zu können, es hat überhaupt nichts mit dem eigentlichen
Programm zu tun.
1) Bei dem Programm hier geht es nur darum um überhaupt erstmal einen
unaligned Access sicher provozieren zu können.Dabei soll es keine
Abhängigkeit zu von irgendwoher gelieferten Daten geben.
@Dr. Sommer: Es geht um Kosten, vorhandenen Quellcode und keine
Anforderungen hinsichtlich der Protierbarkeit. Die zwei ersten Punkte
haben Priorität. Ich hab mir schon nicht umsonst, die Beibehaltung des
unaligned acces ausgesucht.
Ansonsten muss vorhanderner Quellcode umgeschrieben werden, und das ist
kein Dreizeiler. Dabei könnte man in Strukturen mit dem Attribut packed
und byteweisen Zugriff arbeiten. Wenn dann wird das aber der letzte
Rettungsanker.
Decius schrieb:> 1) Bei dem Programm hier geht es nur darum um überhaupt erstmal einen> unaligned Access sicher provozieren zu können.
Wie wäre es hiermit:
Decius schrieb:> Das oben ist nur ein> Beispielprogramm aus dem Internet um sicher einen Unaligned access> provozieren zu können, es hat überhaupt nichts mit dem eigentlichen> Programm zu tun.
Das war mir schon klar, daher die Frage was du eigentlich vorhast.
Decius schrieb:> * Entgegen der Erwartung provoziert das Programm hier auch keinen> * unaligned Access
Wenn ich das richtig sehe sind beide Adressen Vielfache von 2, somit in
Ordnung für 2-Byte-Datentypen.
Decius schrieb:> 2) Ausserdem ist die frage ob ich nur Schritte vergessen habe, eine> Distribution für die Freigabe des unaligned acces umzuarbeiten.
Das klingt nach einem aufwändigen Workaround...
Decius schrieb:> @Dr. Sommer: Es geht um Kosten, vorhandenen Quellcode und keine> Anforderungen hinsichtlich der Protierbarkeit. Die zwei ersten Punkte> haben Priorität.
Ah, der klassische
"Aus-Unwissenheit-lassen-wir-Probleme-sich-ansammeln-damit-wir-später-me
hr-Aufwand-haben"-Ansatz.
Da du partout nicht verraten willst, wo das eigentliche Problem liegt,
hier ein Schuss ins Blaue:
1
uint64_ttest(uint8_t*data){
2
uint64_ti;
3
memcpy(&i,data,sizeof(uint64_t));
4
returni*42;
5
}
Das funktioniert immer, auf jeder Plattform, mit allen
Compiler-Einstellungen/Versionen, egal welches Alignment "data" hat. Es
stürzt niemals ab, es ist lediglich das Ergebnis undefiniert. Falls du
die Daten auf bestimmte statt zufällige Weise interpretieren möchtest,
z.B. annehmen dass die Daten im Little-Endian-Format vorliegen, kannst
du das so machen:
Um Schreibarbeit zu sparen hier nur mit 16 Bits. Das funktioniert
garantiert immer. Kein Herumfrickeln mit Compiler-Optionen und Umbauen
der Linux-Distribution nötig.
>@Programmiergenie:>"Wenn ich das richtig sehe sind beide Adressen Vielfache von 2, somit in>Ordnung für 2-Byte-Datentypen."
Adressraum 0x0000 ... 0x0007
0x0000
0x0001
0x0002
0x0003
0x0004
0x0005
0x0006
0x0007
0x0002 und 0x0006 mögen gerade und durch 2 teilbarsein. So wie ich das
sehe sind es aber dennoch unaligned Adressen. Das Kriterium ist die
Teilbarkeit durch 4........Ahhh, moment........
Arrays sind nicht gepackt oder? Das heisst, bei uint16_t test[2] würden
die Felder auf den Adressen 0x0000 und 0x0004 liegen?
>@Programmiergenie:>Ah, der klassische>"Aus-Unwissenheit-lassen-wir-Probleme-sich-ansammeln-damit-wir-später-m e>hr-Aufwand-haben"-Ansatz.
Ohne komplettes Projektwissen und die vollständige Kenntnis der
Randbedingung, sowie ohne Kenntnis der später gewählten Lösung, sind
solche Beurteilungen sinnlos. Spare Sie Dir also bitte.
>Arrays sind nicht gepackt oder? Das heisst, bei uint16_t test[2] würden>die Felder auf den Adressen 0x0000 und 0x0004 liegen?
Ist so im Debugger nicht nachvollziehbar. Anders als Strukturen liegt
das Array geschlossen hintereinander im Speicher.
Decius schrieb:> So wie ich das> sehe sind es aber dennoch unaligned Adressen.
Nö. Wie bereits erwähnt, steht in Kapitel "A3.2.1 Unaligned data access"
im ARMv7A/R Architecture Reference Manual, dass für 2-Byte-Typen die
Adressen lediglich vielfache von 2 sein müssen. Nur für 4 oder 8-Byte
Typen muss die Adresse ein Vielfaches von 4 sein. Das unterscheidet sich
eben stark nach Plattform. Daher sollte man einfach korrekten Code
schreiben, statt zu pokern dass es auf der einen Plattform doch
irgendwie geht.
Decius schrieb:> Arrays sind nicht gepackt oder? Das heisst, bei uint16_t test[2] würden> die Felder auf den Adressen 0x0000 und 0x0004 liegen?
Doch, Arrays sind gepackt, um Speicher zu sparen. Die Größe eines jeden
Datentyps ist ja so, dass nachfolgende Elemente automatisch korrektes
Alignment haben.
@Dr. Sommer
>Das unterscheidet sich>eben stark nach Plattform. Daher sollte man einfach korrekten Code>schreiben, statt zu pokern dass es auf der einen Plattform doch>irgendwie geht.
Da bin ich Deiner Meinung, allerdings bin ich in meinen Entscheidungen
nicht komplett frei. Ich würde auch gern offener über alles schreiben,
es geht aber nicht.
******************************************
Den von Dir erwähnten Abschnitt "A3.2.1 Unaligned data access" habe ich
mir mal genauer angesehen. Kann es sein das bei SCTL.A=0(vorletzte
Spalte) der GCC trotz der Option -munaligned-access immer noch
Assembler-Befehle benutzt die ein Alignment fault verursachen? Kann man
den GCC, z.B. über eine Option, veranlassen nur Befehle zu benutzen die
kein Alignment fault verursachen? Wenn das Programm sich so nicht
übersetzen lässt, muss es dann eben umgeschrieben werden.
Decius schrieb:> Kann es sein das bei SCTL.A=0(vorletzte> Spalte) der GCC trotz der Option -munaligned-access immer noch> Assembler-Befehle benutzt die ein Alignment fault verursachen?Gerade bei -munaligned-access generiert der GCC Instruktionen, welche
alignment faults produzieren könnten:
-munaligned-access
-mno-unaligned-access
Enables (or disables) reading and writing of 16- and 32- bit values from
addresses that are not 16- or 32- bit aligned. [...] If unaligned access
is not enabled then words in packed data structures are accessed a byte
at a time.
Das bezieht sich aber nur auf "packed" structs (die sind ja ohnehin eine
nicht standardkonforme GCC-Erweiterung). Aber auch mit
-mno-unaligned-access können natürlich noch Instruktionen generiert
werden, die Alignment Faults produzieren. Jeder Zugriff auf
(Halb)Wort-Pointer (LDR/LDRH-Instruktionen) ist ja potentiell unaligned,
und da müsste ja jeder einzelne durch 4 einzelne Byte-Zugriffe
(LDRB-Instruktionen - die einzigen die nie Alignment-Faults
produzieren) und Veroderung realisiert werden, was die Performance
dramatisch verschlechtern würde (das wäre btw äquivalent zur Nutzung
meines obigen memcpy-Beispiels, falls der Compiler das memcpy nicht
wegoptimiert).
Der GCC nimmt nun einmal an, dass man korrekten Code geschrieben hat,
und das bedeutet dass man keine beliebigen char-Pointer auf
2,4,8-Byte-Typen umcastet und dereferenziert. Wenn man das doch tut,
helfen auch Compiler-Optionen nicht...
Ein Workaround wäre wie du schon erwähnt hast die Nutzung von packed
structs und -mno-unaligned-access, aber das ist eben GCC-spezifisch und
nicht portabel (und kann sich in zukünftigen GCC-Versionen auch ändern).