Compiling network code under RHEL5.8 started giving errors when __GNU_SOURCE is defined. The system headers for sys/socket.h etc had been updated to use a gcc feature called transparent unions. This allows functions like connect(2) which take a generic pointer to a specific socket struct to omit the cast. While this makes the caller side simpler (re)implementing the called function requires some understanding and changes.
To get my head around this feature I put together a very simple example.
/* // @(#) transparent_union_example.c // // See http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html // ... // transparent_union // This attribute, attached to a union type definition, indicates // that any function parameter having that union type causes calls to // that function to be treated in a special way. // // First, the argument corresponding to a transparent union type // can be of any type in the union; no cast is required. Also, if the // union contains a pointer type, the corresponding argument can be // a null pointer constant or a void pointer expression; and if the // union contains a void pointer type, the corresponding argument can // be any pointer expression. If the union member type is a pointer, // qualifiers like const on the referenced type must be respected, // just as with normal pointer conversions. // // Second, the argument is passed to the function using the calling // conventions of the first member of the transparent union, not the // calling conventions of the union itself. All members of the union // must have the same machine representation; this is necessary for // this argument passing to work properly. // ... */ # include <stdio.h> enum argtype { T_STRING = 's', T_POINT = 'p', }; typedef enum argtype ARGTYPE; struct point { ARGTYPE type; float x; float y; }; typedef struct point POINT; struct string { ARGTYPE type; char* s; }; typedef struct string STRING; union example { POINT* pt; STRING* str; }; typedef union example EXAMPLE __attribute__ ((__transparent_union__)); void transparent_union_example (EXAMPLE arg) { ARGTYPE type = arg.str->type; /* arg.pt->type would also do */ switch (type) { case T_STRING: { char* s = arg.str->s; printf ("string='%s'\n", s); } break; case T_POINT: { POINT* pt = arg.pt; printf ("p(x,y)=(%f,%f)\n", pt->x, pt->y); } break; } } main() { POINT pt = { T_POINT, 0.5, 7.5 }; STRING str = { T_STRING, "Test" }; transparent_union_example (&str); /* no casts required */ transparent_union_example (&pt); }
Source files
If you are intercepting functions like connect(2) you need to spelunk through sys/socket.h" to get at the union member names. Looking at the definition of __CONST_SOCKADDR_ARG which is just
__const struct sockaddr *for non gnu and older ( < 2.7 gnu) compilers. But for recent gcc the definition is a typedef-ed union
union { __SOCKADDR_ALLTYPES } __CONST_SOCKADDR_ARG __attribute__ ((__transparent_union__)) // ALLTYPES is macro constaining a union member for each socket type // Abreviated here: __SOCKADDR_ALLTYPES __SOCKADDR_ONETYPE(sockaddr) \ __SOCKADDR_ONETYPE(sockaddr_in) \ __SOCKADDR_ONETYPE (sockaddr_un) // And finally __SOCKADDR_ONETYPE does macro magic __SOCKADDR_ONETYPE(type) __const struct type *__restrict __##type##__; // So that __SOCKADDR_ONETYPE(sockaddr_in) expands into: __const struct type *__restrict __sockaddr_in__; // Putting it all together (abreviated) typedef union { __const struct type *__restrict __sockaddr__; __const struct type *__restrict __sockaddr_in__; __const struct type *__restrict __sockaddr_un__; } __CONST_SOCKADDR_ARG __attribute__ ((__transparent_union__));
So reimplimenting connect(2)
int connect (int fd, __CONST_SOCKADDR_ARG addr, socklen_t len) { /* All these are equivalent when using a transparent_union addr.__sockaddr__ -> sa_family; addr.__sockaddr_in__ -> sin_family; addr.__sockaddr_un__ -> sun_family; without the union either addr->sa_family; or (const struct sockaddr_in*) (addr)->sin_family; would have done. */ /* One approach is just revert to the previous way for both. const struct sockaddr* sa = # if defined( USING_TRANSPARENT_UNION) addr.__sockaddr__; # else addr; # endif sa_family_t family = sa->sa_family; ... }
LICENSE
Creative Commons CC0
http://creativecommons.org/publicdomain/zero/1.0/legalcode
AUTHOR
James Sainsbury