Zuweisung an eine member-Variable in einem struct erfolgt an einer falschen Adresse

OP #2538217
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?
Persönliche Seite
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.
OP
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?
OP
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.
OP
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?
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?
OP
> 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
OP
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.
OP
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.
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.
Moderator Persönliche Seite
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.
OP
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 14 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.