Hallo,
bei folgender Verwendung von memcpy bekomme ich einen Speicherschreiber
und das System stürzt ab. Ich komm aber um's Ver.... nicht drauf, wo das
Problem liegt:
Info in pStruct ist vom Typ TypeInfo, pLocalInfo ist vom Typ TypeInfo*.
pLocalInfo zeigt auf eine gültige Speicherstelle.
Es funktioniert komischerweise so:
Wird irgendwas mit dem Alignment sein. pLocalInfo nicht passend für
die Struktur ausgerichtet oder dergleichen.
memcpy() muss der Compiler ja nicht zwingend aufrufen, wenn er den
gleichen Effekt auch auf anderem Wege erreichen kann, d. h. er wird
vermutlich ohne deinen Typecast stattdessen 32-Bit-Operationen
erzeugen, die dann zum Crash führen.
tatsächlich unterschiedlich übersetzt werden?
Ich kann mir das nicht vorstellen und glaube deswegen, dass der Fehler
ganz woanders liegt und dieser Fehler sporadisch zu einem ungültigen
Pointer in pStruct, pStruct->Info und/oder pLocalInfo führt.
Jörg W. schrieb:> Wird irgendwas mit dem Alignment sein. pLocalInfo nicht passend für> die Struktur ausgerichtet oder dergleichen.>> memcpy() muss der Compiler ja nicht zwingend aufrufen, wenn er den> gleichen Effekt auch auf anderem Wege erreichen kann, d. h. er wird> vermutlich ohne deinen Typecast stattdessen 32-Bit-Operationen> erzeugen, die dann zum Crash führen.
Da gab's doch mal was...
Bug 53016 - memcpy optimization can cause unaligned access on ARMhttps://gcc.gnu.org/bugzilla/show_bug.cgi?id=53016
Mit dem schönen Status: RESOLVED INVALID
Arc N. schrieb:> Mit dem schönen Status: RESOLVED INVALID
Völlig verständlich für mich.
Wenn der Programmierer behauptet, das Ding könne auf struct xyz
zeigen, es ist aber dafür am Ende doch nicht passend ausgerichtet,
dann hat sich der Programmierer geirrt, nicht der Compiler.
Wenn man es partout nicht will, dass der Compiler Optimierungen in
Funktionen der Standardbibliothek vornimmt, kann man natürlich immer
im freestanding mode compilieren (-ffreestanding) – aber damit
verliert man dann alle derartigen Optimierungen auf einmal.
Ich finde es allemal sinnvoller, dass man seine Zeiger so passend
sortiert, dass sie die Alignment-Anforderungen auch einhalten.
Alternativ wurde ja der passende Würgaround schon eingangs genannt:
wenn man den nicht ordentlich ausgerichteten Zeiger dem Compiler
explizit als "uint8_t *" deklariert, dann benutzt er ihn auch so.
Jörg W. schrieb:> Arc N. schrieb:>> Mit dem schönen Status: RESOLVED INVALID>> Völlig verständlich für mich.
Ich kann die Idee hinter dieser Optimierung verstehen und finde sie
durchaus auch sinnvoll, aber frage mich, ob sie nicht gegen die
ISO-C-Norm verstößt. Memcpy bekommt immerhin als Quelle und Ziel Zeiger
auf void geliefert, und laut ISO C "The memcpy function copies n
characters from the object pointed to by s2 into the object pointed to
by s1" und "A pointer to void shall have the same representation and
alignment requirements as a pointer to a character type".
Ich versteh noch nicht so ganz was du für welche Typen hast.
Wenn man sich die Informationen erst irgendwie zusammensuchen musst,
klappt das nicht.
Es gibt ja auch sowas wie online C Compiler, inkl. share Funktion.
Ich hab das mal so gemacht wie es deinen Text verstanden habe (scheiß
text), und bei mir funktionierts. siehe
http://www.tutorialspoint.com/compile_c_online.php?PID=0Bw_CjBb95KQMTWxoNnhvZTllNTg
Jörg W. schrieb:> Wenn man es partout nicht will, dass der Compiler Optimierungen in> Funktionen der Standardbibliothek vornimmt, kann man natürlich immer> im freestanding mode compilieren (-ffreestanding)
-ffreestanding will man ziemlich sicher nicht wirklich.
Wenn dann eher -fno-builtin. Oder noch weniger gießkannig
-fno-builtin-memcpy.
Aber bei Fällen wie Code von PR53016 ist natürlich nicht das die lösung,
sondern gültigen Code zu schreiben.
Rolf M. schrieb:> Ich kann die Idee hinter dieser Optimierung verstehen und finde sie> durchaus auch sinnvoll, aber frage mich, ob sie nicht gegen die> ISO-C-Norm verstößt.
Im Bugreport steht deutlich drin, daß die Norm da "undefined behaviour"
sagt. Also kein Verstoß.
> Memcpy bekommt immerhin als Quelle und Ziel Zeiger> auf void geliefert,
Äh nein, das waren hier Zeiger auf "struct TypeInfo", die 32bit aligned
ist. Und auch ein Cast auf void-Pointer würde das Alignment nicht
aufheben, steht auch im Bugreport drin.
Dort wird auch die IMHO sinnvollste Methode gezeigt: die struct mit
"__attribute__((packed))" deklarieren, so daß der Compiler weiß, daß du
da mit non-aligned Pointern rumhantieren willst.
Ralf D. schrieb:> Rolf M. schrieb:>> Ich kann die Idee hinter dieser Optimierung verstehen und finde sie>> durchaus auch sinnvoll, aber frage mich, ob sie nicht gegen die>> ISO-C-Norm verstößt.>> Im Bugreport steht deutlich drin, daß die Norm da "undefined behaviour"> sagt. Also kein Verstoß.
Hmm, ok. Ich war davon ausgegangen, dass erst der Zugriff über einen
Pointer mit falschem Alignment UB auslöst, aber es reicht dafür wohl
schon seine reine Existenz. Das heißt, die Ursache des UB liegt schon
weit vor der eigentlichen Nutzung von memcpy, dort wo ein Zeiger auf
TypeInfo mit falschem Alignment angelegt wird.
> Dort wird auch die IMHO sinnvollste Methode gezeigt: die struct mit> "__attribute__((packed))" deklarieren, so daß der Compiler weiß, daß du> da mit non-aligned Pointern rumhantieren willst.
Das verändert dann aber ggf. das Layout der Struktur. Und möglicherweise
wollte der TE memcpy hier benutzen, um das ganze an eine Stelle mit
korrektem Alignment zu kopieren. Dann ist durch __attribute__((packed))
unter Umständen das Alignment einzelner Elemente der Struktur falsch.
Rolf M. schrieb:> Ralf D. schrieb:>> Dort wird auch die IMHO sinnvollste Methode gezeigt: die struct mit>> "__attribute__((packed))" deklarieren,>> Das verändert dann aber ggf. das Layout der Struktur.
__attribute__((__aligned__(1)))