Hallo,
ich beschäftige mich gerade mit Sockets in C auf einem NIOS
II-Prozessor.
Dabei habe ich das folgende Problem:
1
structsockaddr{
2
U16sa_family;/* address family */
3
charsa_data[14];/* up to 14 bytes of direct address */
4
};
5
6
/* Berkeley style "Internet address" */
7
structin_addr{
8
U32s_addr;
9
};
10
11
/* Berkeley style "Socket address" */
12
structsockaddr_in{
13
shortsin_family;
14
U16sin_port;
15
structin_addrsin_addr;
16
charsin_zero[8];
17
};
18
19
voidsetSockAddr(structsockaddr*sockAddrStorage)
20
{
21
structsockaddr_in*sockaddr_in;
22
void*a;
23
24
sockaddr_in=(structsockaddr_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?
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.
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.
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
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.
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.
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
typedefstructal_sockaddr
2
{
3
uint8_tsa_data[16];
4
}al_sockaddr_t
5
6
structsockaddr{
7
U16sa_family;/* address family */
8
charsa_data[14];/* up to 14 bytes of direct address */
9
};
10
11
/* Berkeley style "Internet address" */
12
structin_addr{
13
U32s_addr;
14
};
15
16
/* Berkeley style "Socket address" */
17
structsockaddr_in{
18
shortsin_family;
19
U16sin_port;
20
structin_addrsin_addr;
21
charsin_zero[8];
22
};
23
24
voidsetSockAddr(al_sockaddr_t*sockAddrStorage)
25
{
26
structsockaddr_in*sockaddr_in;
27
void*a;
28
29
sockaddr_in=(structsockaddr_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?
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...
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?
> 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:
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
structsockaddr_in{
2
shortsin_family;
3
U16sin_port;
4
structin_addrsin_addr;
5
charsin_zero[8];
6
};
4-Byte-aligned ist, während
1
typedefstructal_sockaddr
2
{
3
uint8_tsa_data[16];
4
}al_sockaddr_t
Byte-aligned ist.
D.h. es könnte doch eigentlich prinzipiell festgestellt werden, dass es
beim Casten
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.
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.
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.
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.