authorgravatar for me@jo.ieinvlpg <me@jo.ie> 2026-02-28 15:01:05+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-02 17:46:26+01:00
log0a2f6a048b1e9c9eee97b463c18aaedad3e31f60
treedbdfef9a9f266e1c8468abe06dd5ddcac2b9e536
parent442b292a3712829578c1b93d406a87f1abcf45b8

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)); } ```

1 files changed, 37 insertions(+), 10 deletions(-)

lib/std/c.zig+37-10
......@@ -4186,18 +4186,45 @@ pub const msghdr = switch (native_os) {
41864186 else => void,
41874187};
41884188
4189/// There are several instances of struct fields that POSIX defines as either int or socklen_t, but
4190/// on Linux are size_t. glibc ignores POSIX, and uses the Linux kernel's definitions. musl on the
4191/// other hand aims to be POSIX-ly correct, and defines those fields in a manner aligning with
4192/// POSIX.
4193///
4194/// musl works around this incompatibility between the 64-bit Linux ABI and the POSIX specification
4195/// by adding padding fields on either side depending on host endianness:
4196///
4197/// #if __LONG_MAX > 0x7fffffff && __BYTE_ORDER == __BIG_ENDIAN
4198/// int __pad2;
4199/// #endif
4200/// socklen_t msg_controllen;
4201/// #if __LONG_MAX > 0x7fffffff && __BYTE_ORDER == __LITTLE_ENDIAN
4202/// int __pad2;
4203/// #endif
4204///
4205/// To emulate this quirk of musl, the MuslOnlyPadding field is used in these structs
4206///
4207/// pad0: MuslOnlyPadding(.big) = 0,
4208/// msg_controllen: socklen_t,
4209/// pad1: MuslOnlyPadding(.little) = 0,
4210///
4211/// On 32-bit and non-musl systems, these fields will be zero sized, and ignored.
4212fn MuslOnlyPadding(endian: std.builtin.Endian) type {
4213 return if (builtin.abi.isMusl() and @sizeOf(usize) == 8 and native_endian == endian) u32 else u0;
4214}
4215
41894216/// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html
41904217const posix_msghdr = extern struct {
41914218 name: ?*sockaddr,
41924219 namelen: socklen_t,
41934220 iov: [*]iovec,
4194 pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,
4221 pad0: MuslOnlyPadding(.big) = 0,
41954222 iovlen: u32,
4196 pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,
4223 pad1: MuslOnlyPadding(.little) = 0,
41974224 control: ?*anyopaque,
4198 pad2: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,
4225 pad2: MuslOnlyPadding(.big) = 0,
41994226 controllen: socklen_t,
4200 pad3: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,
4227 pad3: MuslOnlyPadding(.little) = 0,
42014228 flags: u32,
42024229};
42034230
......@@ -4226,13 +4253,13 @@ const posix_msghdr_const = extern struct {
42264253 name: ?*const sockaddr,
42274254 namelen: socklen_t,
42284255 iov: [*]const iovec_const,
4229 pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,
4256 pad0: MuslOnlyPadding(.big) = 0,
42304257 iovlen: u32,
4231 pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,
4258 pad1: MuslOnlyPadding(.little) = 0,
42324259 control: ?*const anyopaque,
4233 pad2: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,
4260 pad2: MuslOnlyPadding(.big) = 0,
42344261 controllen: socklen_t,
4235 pad3: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,
4262 pad3: MuslOnlyPadding(.little) = 0,
42364263 flags: u32,
42374264};
42384265
......@@ -4276,9 +4303,9 @@ pub const cmsghdr = switch (native_os) {
42764303};
42774304
42784305const posix_cmsghdr = extern struct {
4279 pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,
4306 pad0: MuslOnlyPadding(.big) = 0,
42804307 len: socklen_t,
4281 pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,
4308 pad1: MuslOnlyPadding(.little) = 0,
42824309 level: c_int,
42834310 type: c_int,
42844311};