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