setsockopt MCAST_JOIN_GROUP calls (et cetera) fail with EINVAL when my 32-bit binary talks to a 64-bit kernel. this is because struct group_req isn't packed. /tmp$ cat mc.cpp #include <errno.h> #include <inttypes.h> #include <signal.h> #include <stdbool.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> int main() { struct sockaddr_in addr; memset (&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (-1 == recv_sock) { perror("socket failed"); return 1; } struct sockaddr_in recv_addr; memcpy (&recv_addr, &addr, sizeof(addr)); recv_addr.sin_port = 7500; socklen_t l = sizeof(recv_addr); if (0 != bind (recv_sock, (struct sockaddr*)&recv_addr, l)) { perror("bind failed"); return 1; } struct group_req gr; memset (&gr, 0, sizeof(gr)); printf("sizeof(group_req)=%i\n", sizeof(group_req)); printf("offsetof(group_req, gr_interface)=%i\n", offsetof(group_req, gr_interface)); printf("offsetof(group_req, gr_group)=%i\n", offsetof(group_req, gr_group)); sockaddr_storage ss; printf("sizeof(ss.ss_family)=%i\n", sizeof(ss.ss_family)); ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; if (0 != setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr))) { perror("setsockopt failed"); return 1; } return 0; } /tmp$ g++ -m64 mc.cpp -o mc && ./mc sizeof(group_req)=136 offsetof(group_req, gr_interface)=0 offsetof(group_req, gr_group)=8 sizeof(ss.ss_family)=2 /tmp$ g++ -m32 mc.cpp -o mc && ./mc sizeof(group_req)=132 offsetof(group_req, gr_interface)=0 offsetof(group_req, gr_group)=4 sizeof(ss.ss_family)=2 setsockopt failed: Invalid argument /tmp$ i've worked around this by changing my code to recognize EINVAL and try again with its own fake struct instead struct group_req64 { uint32_t gr_interface; uint32_t my_padding; sockaddr_storage gr_group; }; that works, but i wonder if glibc should be doing this for me? i know it does stuff with statfs/statfs64 and suchlike.
This is a kernel bug, it does not provide the necessary compat glue.