Forum: PC-Programmierung memcpy funktioniert nicht


von M. I. (seventh_son)


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?

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


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.

von a.s (Gast)


Lesenswert?

Auf was ist denn pLocalInfo gesetzt? Also, ist der Pointer korrekt 
erzeugt?

Hast Du mindestens Wall mal versucht?

von Yalu X. (yalu) (Moderator)


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.

: Bearbeitet durch Moderator
von Arc N. (arc)


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

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


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.

von Rolf M. (rmagnus)


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".

: Bearbeitet durch User
von ui (Gast)


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

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


Lesenswert?

Rolf M. schrieb:
> aber frage mich, ob sie nicht gegen die ISO-C-Norm verstößt.

Deiner Argumentation würde ich folgen.

von Johann L. (gjlayde) Benutzerseite


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.

: Bearbeitet durch User
von Ralf D. (doeblitz)


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.

von Rolf M. (rmagnus)


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.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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)))

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.