memcpy funktioniert nicht

OP #4984202
Lesenswert?

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:
1
typedef struct _tag_TypeInfo
2
{
3
    uint32_t var0;
4
    OwnType var1;
5
    OwnType2 var2;
6
}TypeInfo;  
7

8
memcpy(&(pStruct->Info),pLocalInfo,sizeof(TypeInfo));

Info in pStruct ist vom Typ TypeInfo, pLocalInfo ist vom Typ TypeInfo*. 
pLocalInfo zeigt auf eine gültige Speicherstelle.

Es funktioniert komischerweise so:
1
memcpy(&(pStruct->Info),(uint8_t*)pLocalInfo,sizeof(TypeInfo));

und so:
1
TypeInfo tempInfo;
2

3
memcpy(&(tempInfo), pLocalInfo,sizeof(TypeInfo));

Seht ihr in der Verwendung irgendein Problem, oder muss ich den Fehler 
in pStruct->Info suchen?
Moderator Persönliche Seite #4984207
Lesenswert?

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.
Moderator #4984236
Lesenswert?

Hast du mal im Assemblerlisting nachgeschaut, ob

1
memcpy(&(pStruct->Info),pLocalInfo,sizeof(TypeInfo));

und

1
memcpy(&(pStruct->Info),(uint8_t*)pLocalInfo,sizeof(TypeInfo));

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

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 ARM
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53016
Mit dem schönen Status: RESOLVED INVALID
Moderator Persönliche Seite #4984704
Lesenswert?

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

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

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
Persönliche Seite #4985086
Lesenswert?

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

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

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.

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