From 0a2f6a048b1e9c9eee97b463c18aaedad3e31f60 Mon Sep 17 00:00:00 2001 From: invlpg Date: Sat, 28 Feb 2026 15:01:05 +0100 Subject: [PATCH] fix msghdr and cmsghdr on non-Linux targets, document musl behaviour The following assertions fail on non-Linux platforms after c0c20105354 which inserted padding based on musl definitions. This padding only exists on musl to workaround a discrepancy betweeen the POSIX API and Linux ABI, and is incorrect on other POSIX operating systems. This change makes the padding musl-only, and documents the reason it exists. With this change, the assertions pass on Linux and FreeBSD targets. The corresponding definitions on other targets line up with the POSIX and FreeBSD ones, so they should work there too. ```zig const std = @import("std"); const assert = std.debug.assert; const msghdr = std.c.msghdr; const cmsghdr = std.c.cmsghdr; const c = @cImport({ @cInclude("sys/socket.h"); }); comptime { assert(@offsetOf(msghdr, "iovlen") == @offsetOf(c.msghdr, "msg_iovlen")); assert(@offsetOf(msghdr, "controllen") == @offsetOf(c.msghdr, "msg_controllen")); assert(@offsetOf(msghdr, "control") == @offsetOf(c.msghdr, "msg_control")); assert(@offsetOf(msghdr, "flags") == @offsetOf(c.msghdr, "msg_flags")); assert(@sizeOf(msghdr) == @sizeOf(c.msghdr)); assert(@offsetOf(cmsghdr, "len") == @offsetOf(c.cmsghdr, "cmsg_len")); assert(@offsetOf(cmsghdr, "level") == @offsetOf(c.cmsghdr, "cmsg_level")); assert(@sizeOf(cmsghdr) == @sizeOf(c.cmsghdr)); } ``` --- lib/std/c.zig | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 653e10576d..8251b17913 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -4186,18 +4186,45 @@ pub const msghdr = switch (native_os) { else => void, }; +/// There are several instances of struct fields that POSIX defines as either int or socklen_t, but +/// on Linux are size_t. glibc ignores POSIX, and uses the Linux kernel's definitions. musl on the +/// other hand aims to be POSIX-ly correct, and defines those fields in a manner aligning with +/// POSIX. +/// +/// musl works around this incompatibility between the 64-bit Linux ABI and the POSIX specification +/// by adding padding fields on either side depending on host endianness: +/// +/// #if __LONG_MAX > 0x7fffffff && __BYTE_ORDER == __BIG_ENDIAN +/// int __pad2; +/// #endif +/// socklen_t msg_controllen; +/// #if __LONG_MAX > 0x7fffffff && __BYTE_ORDER == __LITTLE_ENDIAN +/// int __pad2; +/// #endif +/// +/// To emulate this quirk of musl, the MuslOnlyPadding field is used in these structs +/// +/// pad0: MuslOnlyPadding(.big) = 0, +/// msg_controllen: socklen_t, +/// pad1: MuslOnlyPadding(.little) = 0, +/// +/// On 32-bit and non-musl systems, these fields will be zero sized, and ignored. +fn MuslOnlyPadding(endian: std.builtin.Endian) type { + return if (builtin.abi.isMusl() and @sizeOf(usize) == 8 and native_endian == endian) u32 else u0; +} + /// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html const posix_msghdr = extern struct { name: ?*sockaddr, namelen: socklen_t, iov: [*]iovec, - pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0, + pad0: MuslOnlyPadding(.big) = 0, iovlen: u32, - pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0, + pad1: MuslOnlyPadding(.little) = 0, control: ?*anyopaque, - pad2: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0, + pad2: MuslOnlyPadding(.big) = 0, controllen: socklen_t, - pad3: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0, + pad3: MuslOnlyPadding(.little) = 0, flags: u32, }; @@ -4226,13 +4253,13 @@ const posix_msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, iov: [*]const iovec_const, - pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0, + pad0: MuslOnlyPadding(.big) = 0, iovlen: u32, - pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0, + pad1: MuslOnlyPadding(.little) = 0, control: ?*const anyopaque, - pad2: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0, + pad2: MuslOnlyPadding(.big) = 0, controllen: socklen_t, - pad3: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0, + pad3: MuslOnlyPadding(.little) = 0, flags: u32, }; @@ -4276,9 +4303,9 @@ pub const cmsghdr = switch (native_os) { }; const posix_cmsghdr = extern struct { - pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0, + pad0: MuslOnlyPadding(.big) = 0, len: socklen_t, - pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0, + pad1: MuslOnlyPadding(.little) = 0, level: c_int, type: c_int, };