[mpls-linux-general] Endianess and bitfields
Status: Beta
Brought to you by:
jleu
|
From: Georg K. <gk...@gi...> - 2002-10-29 21:00:10
|
Hi all,
today I was wrestling with some endian issues in the MPLS kernel patch.
Especially the following bitfield definition is interesting:
struct mpls_gen_key {
unsigned int index:10;
unsigned int gen:20;
unsigned int type:2;
};
The C language does not define exactly how these bits are stored into
memory. It might either be right-to-left assigned or left-to-right
assigned depending on the machine or even on the compiler used. So using
this kind of struct is not portable in general. But looking into the
kernel sources there are also such structure definitions (i.e. struct iphdr
in include/linux/ip.h where the first byte of the ip header is defined as
a bitfield).
Anyway gcc on powerpc uses left-to-right assigned bit-fields, which
causes the struct above not to work correctly on powerpc.
Here is what the struct would look like on powerpc machines:
Byte 0 Byte 1 Byte 2 Byte 3
76543210 76543210 76543210 76543210
| index || ----------- gen -------|||< (last 2 bits are type)
On the little endian x86 architecture with right-to-left assigned bit-
fields the struct look like:
Byte 0 Byte 1 Byte 2 Byte 3
76543210 76543210 76543210 76543210
|in-lo | |g-lo||| | g-mid| |||g-hi|
So the original fields are stored as follows:
index: Byte1[Bits 1-0] Byte0[Bits 7-0]
gen: Byte3[Bits 5-0] Byte2[Bits 7-0] Byte1[Bits 7-2]
type Byte3[Bits 7-6]
This looks rather complex but after byte swapping which must be applied
on little-endian machines before sending the data on the wire it looks
as follows:
Byte 0 Byte 1 Byte 2 Byte 3
76543210 76543210 76543210 76543210
||| ------- gen -----------||-----index-|
which is what we wanted.
As we saw from the C standard there is no good solution working under
any circumstances. But the kernel uses such structs with the define
__LITTLE_ENDIAN_BITFIELD, and therfore a solution to make the struct
above working on big endian machines too would be:
struct mpls_gen_key {
#if defined (__LITTLE_ENDIAN_BITFIELD)
unsigned int index:10;
unsigned int gen:20;
unsigned int type:2;
#else
unsigned int type:2;
unsigned int gen:20;
unsigned int index:10;
#endif
};
What do you think?
Kind regards,
Georg Klug
|