Forum: Compiler & IDEs Zuweisung an eine member-Variable in einem struct erfolgt an einer falschen Adresse


von Frank H. (gowi)


Lesenswert?

Hallo,
ich beschäftige mich gerade mit Sockets in C auf einem NIOS 
II-Prozessor.

Dabei habe ich das folgende Problem:
1
struct sockaddr {
2
   U16     sa_family;     /* address family */
3
   char     sa_data[14];      /* up to 14 bytes of direct address */
4
};
5
6
/* Berkeley style "Internet address" */
7
struct in_addr {
8
   U32  s_addr;
9
};
10
11
/* Berkeley style "Socket address" */
12
struct sockaddr_in {
13
  short    sin_family;
14
  U16      sin_port;
15
  struct   in_addr  sin_addr;
16
  char     sin_zero[8];
17
};
18
19
void setSockAddr( struct sockaddr* sockAddrStorage )
20
{
21
struct sockaddr_in * sockaddr_in;
22
void * a;
23
24
sockaddr_in = ( struct sockaddr_in * ) sockAddrStorage;
25
26
a = &(sockaddr_in->sin_addr.s_addr); // a zeigt dann auf 0x08177a32
27
28
sockaddr_in->sin_addr.s_addr = 0xFF;
29
}

Die Speicheradresse des Members sin_addr.s_addr ist 0x08177a32. Der Wert 
0xFF wird aber an die Adresse 0x8177a30 geschrieben. Der Speicher an der 
Adresse 0x8177a30 gehört aber zum Member sin_port. Hat jemand eine 
Erklärung für dieses für mich merkwürdige Verhalten?

von Andreas B. (andreas_b77)


Lesenswert?

Merkwürdig. Ist das mit dem Debugger festgestellt? Falls ja, läuft dann 
auf dem Nios auch genau das Binary mit dem der Debugger arbeitet?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Strukturen sind nicht gepackt, sprich, zugunsten des Alignments 
werden Dummybytes zwischen die einzelnen Strukturelemente eingefügt, 
damit mit glatten 32-Bit-Zugriffen gearbeitet werden kann.

Vergleiche einfach mal das, was sizeof von Deinen Strukturdefinitionen 
hält und was Du durch Auszählen der Elemente herausbekommst.

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


Lesenswert?

Rufus Τ. Firefly schrieb:
> Die Strukturen sind nicht gepackt

Das würde nur einen Zugriff "weiter hinten" erklären, nicht aber,
dass der Compiler eine kleinere Adresse zugreift als erwartet.

von Frank H. (gowi)


Lesenswert?

1
struct sockaddr_in {
2
  short    sin_family;         //  2 Byte
3
  U16      sin_port;           //  2 Byte
4
  struct   in_addr  sin_addr;  //  4 Byte
5
  char     sin_zero[8];        //  8 Byte
6
};

sizeof(sockaddr_in) = 16


Es scheinen also keine Padding-bytes im struct eingefügt zu sein. Aber 
auch wenn das so sein sollte, das erklärt doch nicht, warum die 
Anweisung
1
sockaddr_in->sin_addr.s_addr = 0xFF;

den Wert 0xFF an die Adresse 0x08177a30 schreibt, während ich bei
1
a = &(sockaddr_in->sin_addr.s_addr);

für a den Wert 0x08177a32 erhalte?

von Frank H. (gowi)


Lesenswert?

Kann dieses Problem irgendwas mit dem memory alignment zu tun haben? Mir 
ist aufgefallen, dass alles problemlos funktioniert, wenn 
sockaddr_in->sin_addr.s_addr an einer durch 4 teilbaren Adresse liegt. 
Im obigen Beispiel ist das nicht der Fall.

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


Lesenswert?

Ja, kann, aber sofern man den Compiler nicht aktiv davon abhält,
sollte er sich um das notwendige alignment eigentlich selbst kümmern.

von Udo S. (urschmitt)


Lesenswert?

Frank Hans schrieb:
> Kann dieses Problem irgendwas mit dem memory alignment zu tun haben?
Das hat Rufus ja schon gesagt aber wie Jörg dann gesagt hat würde das 
nur einen Zugriff an einer größeren Speicherstelle erklären, nicht aber 
weiter vorne.
Irgendwas übersehen wir da noch oder da wäre wirklich ein 
Compilerfehler.
Aber das glaube ich erst ganz zum Schluss.

von Udo S. (urschmitt)


Lesenswert?

Blöde Idee:
Was passiert wenn du statt 0xff den Wert 0x11224488 reinschreibst.
Wo steht dann welches Byte?

von Frank H. (gowi)


Lesenswert?

Eventuell komme ich einer Antwort näher. Es ist wohl so, dass der NIOS 
auf 4-Byte Daten nur zugreifen kann, wenn die Adresse ein Vielfaches von 
4 ist (data-misalignment wird nicht unterstützt).

Das obige Beispiel war nicht ganz vollständig. Es müsste lauten:
1
typedef struct al_sockaddr
2
{
3
    uint8_t         sa_data[16];
4
} al_sockaddr_t
5
6
struct sockaddr {
7
   U16     sa_family;     /* address family */
8
   char     sa_data[14];      /* up to 14 bytes of direct address */
9
};
10
11
/* Berkeley style "Internet address" */
12
struct in_addr {
13
   U32  s_addr;
14
};
15
16
/* Berkeley style "Socket address" */
17
struct sockaddr_in {
18
  short    sin_family;
19
  U16      sin_port;
20
  struct   in_addr  sin_addr;
21
  char     sin_zero[8];
22
};
23
24
void setSockAddr( al_sockaddr_t* sockAddrStorage )
25
{
26
struct sockaddr_in * sockaddr_in;
27
void * a;
28
29
sockaddr_in = ( struct sockaddr_in * ) sockAddrStorage;
30
31
a = &(sockaddr_in->sin_addr.s_addr); // a zeigt dann auf 0x08177a32
32
33
sockaddr_in->sin_addr.s_addr = 0xFF;
34
}

Meine Vermutung: sockAddrStorage ist Byte-Aligned - kann also 
prinzipiell an jeder Speicheradresse stehen. sockaddr_in muss aber 
4-Byte-Aligned sein, damit die Member entsprechend auch richtig aligned 
sind. Durch das Casten nimmt sockaddr_in aber das Alignment von 
sockAddrStorage an und daher kann u.U. das Alignment nicht mehr stimmen. 
Sehe ich das so richtig?

von Uli T. (avaron)


Lesenswert?

sockAddrStorage ist mal nur ein Pointer, der kann auf alles zeigen, also 
Byte-Aligned.
sockaddr_in muß 4-Byte alligned liegen, wenn Du 4-Byte zugriffe machst.

*((unit8_t*)&(sockaddr_in->sin_addr.s_addr)) = 0xFF;

sollte eigentlich auch gehen...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Welche Endianess hat das Ding denn?

von Frank H. (gowi)


Lesenswert?

Johann L. schrieb:
> Welche Endianess hat das Ding denn?

Little Endian. Die notwendigen htonl()-Funktionen usw. kommen noch.

von Rolf M. (rmagnus)


Lesenswert?

Frank Hans schrieb:
> Meine Vermutung: sockAddrStorage ist Byte-Aligned - kann also
> prinzipiell an jeder Speicheradresse stehen. sockaddr_in muss aber
> 4-Byte-Aligned sein, damit die Member entsprechend auch richtig aligned
> sind. Durch das Casten nimmt sockaddr_in aber das Alignment von
> sockAddrStorage an und daher kann u.U. das Alignment nicht mehr stimmen.
> Sehe ich das so richtig?

Kann im Prinzip sein. Die Frage bleibt, warum sockAddrStorage ein 
falsches Alignment hat. Das passiert normalerweise nicht, weil sich, wie 
schon geschrieben wurde, der Compiler eigentlich selbst um das korrekte 
Algiment kümmert, wenn man nicht noch irgendwelche "Schweiereien" 
einbaut, die das verhindern. Wo kommt denn die Struktur her und wie wird 
sie erzeugt?

von Frank H. (gowi)


Lesenswert?

> Kann im Prinzip sein. Die Frage bleibt, warum sockAddrStorage ein
> falsches Alignment hat. Das passiert normalerweise nicht, weil sich, wie
> schon geschrieben wurde, der Compiler eigentlich selbst um das korrekte
> Algiment kümmert, wenn man nicht noch irgendwelche "Schweiereien"
> einbaut, die das verhindern. Wo kommt denn die Struktur her und wie wird
> sie erzeugt?

sockAddrStorage ist vom Typ al_sockaddr_t:
1
typedef struct al_sockaddr
2
{
3
    uint8_t         sa_data[16];
4
} al_sockaddr_t

von Frank H. (gowi)


Lesenswert?

Gelöst habe ich das Problem erstmal, indem ich das Alignment von 
al_sockaddr_t manuell vorgebe:
1
typedef struct al_sockaddr
2
{
3
    uint8_t         sa_data[16];
4
} al_sockaddr_t __attribute__ ((aligned (4)));

Ich frage mich aber, ob ich nicht eine Compiler-Warnung aufgrund des 
nicht übereinstimmenden Alignments beim Casten erhalten müsste.

von Uli T. (avaron)


Lesenswert?

Der Compiler weiß zur compile time nicht wie das was Du castest alligned 
ist, wie soll er da warnen?

von Frank H. (gowi)


Lesenswert?

Uli Trautenberg schrieb:
> Der Compiler weiß zur compile time nicht wie das was Du castest alligned
> ist, wie soll er da warnen?

Hmm. Beim Kompilieren müsste doch feststehen, dass
1
struct sockaddr_in {
2
  short    sin_family;
3
  U16      sin_port;
4
  struct   in_addr  sin_addr;
5
  char     sin_zero[8];
6
};

4-Byte-aligned ist, während
1
typedef struct al_sockaddr
2
{
3
    uint8_t         sa_data[16];
4
} al_sockaddr_t
 Byte-aligned ist.

D.h. es könnte doch eigentlich prinzipiell festgestellt werden, dass es 
beim Casten
1
sockaddr_in = ( struct sockaddr_in * ) sockAddrStorage;
 zu Problemen kommen könnte.

von Rolf M. (rmagnus)


Lesenswert?

Frank Hans schrieb:
> sockAddrStorage ist vom Typ al_sockaddr_t:
> typedef struct al_sockaddr
> {
>     uint8_t         sa_data[16];
> } al_sockaddr_t

Ach das sehe ich jetzt erst. Dann ist das logisch. Warum ist das denn so 
definiert?

Frank Hans schrieb:
> Byte-aligned ist.
>
> D.h. es könnte doch eigentlich prinzipiell festgestellt werden, dass es
> beim Castensockaddr_in = ( struct sockaddr_in * ) sockAddrStorage; zu
> Problemen kommen könnte.

Das ist die Sache mit Casts in C. Mit einem Cast sagst du deinem 
Compiler soviel wie: "Auch wenn das deiner Meinung nach nicht paßt, 
mach's trotzdem so. Ich weiß, was ich tue!". Und dann ist der Compiler 
eben still.

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


Lesenswert?

Rolf Magnus schrieb:
> Mit einem Cast sagst du deinem
> Compiler soviel wie: "Auch wenn das deiner Meinung nach nicht paßt,
> mach's trotzdem so. Ich weiß, was ich tue!".

Das ist genau das, was ich weiter oben gemeint habe mit "sofern man
den Compiler nicht aktiv davon abhält".  Wenn man den Speicherplatz
natürlich für Bytes anfordert, dann aber auf eine Socket-Adresse
"umwidmet", dann muss man sich nicht weiter wundern.

von Frank H. (gowi)


Lesenswert?

Rolf Magnus schrieb:
> Frank Hans schrieb:
>> sockAddrStorage ist vom Typ al_sockaddr_t:
>> typedef struct al_sockaddr
>> {
>>     uint8_t         sa_data[16];
>> } al_sockaddr_t
>
> Ach das sehe ich jetzt erst. Dann ist das logisch. Warum ist das denn so
> definiert?

Die Definiton kommt aus dem DPWS-Stack (Device Profile for Web 
Services), den ich benutze. Dort sollte die Definiton wohl möglichst 
allgemein gehalten werden.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:

> Kann im Prinzip sein. Die Frage bleibt, warum sockAddrStorage ein
> falsches Alignment hat. Das passiert normalerweise nicht,

Das passiert ganz von allein, wenn der Kram in einem korrekt alignten 
Ethernet Frame sitzt, dessen Header aber leider 14 Bytes lang ist. 
Schlauköpfe sorgen deshalb dafür, dass der Puffer vom Frame passend 
misaligned im Speicher liegt, also an (addr % 4) == 2.

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.