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) {...@@ -4186,18 +4186,45 @@ pub const msghdr = switch (native_os) {
4186 else => void,4186 else => void,
4187};4187};
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
4189/// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html4216/// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html
4190const posix_msghdr = extern struct {4217const posix_msghdr = extern struct {
4191 name: ?*sockaddr,4218 name: ?*sockaddr,
4192 namelen: socklen_t,4219 namelen: socklen_t,
4193 iov: [*]iovec,4220 iov: [*]iovec,
4194 pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,4221 pad0: MuslOnlyPadding(.big) = 0,
4195 iovlen: u32,4222 iovlen: u32,
4196 pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,4223 pad1: MuslOnlyPadding(.little) = 0,
4197 control: ?*anyopaque,4224 control: ?*anyopaque,
4198 pad2: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,4225 pad2: MuslOnlyPadding(.big) = 0,
4199 controllen: socklen_t,4226 controllen: socklen_t,
4200 pad3: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,4227 pad3: MuslOnlyPadding(.little) = 0,
4201 flags: u32,4228 flags: u32,
4202};4229};
42034230
...@@ -4226,13 +4253,13 @@ const posix_msghdr_const = extern struct {...@@ -4226,13 +4253,13 @@ const posix_msghdr_const = extern struct {
4226 name: ?*const sockaddr,4253 name: ?*const sockaddr,
4227 namelen: socklen_t,4254 namelen: socklen_t,
4228 iov: [*]const iovec_const,4255 iov: [*]const iovec_const,
4229 pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,4256 pad0: MuslOnlyPadding(.big) = 0,
4230 iovlen: u32,4257 iovlen: u32,
4231 pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,4258 pad1: MuslOnlyPadding(.little) = 0,
4232 control: ?*const anyopaque,4259 control: ?*const anyopaque,
4233 pad2: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,4260 pad2: MuslOnlyPadding(.big) = 0,
4234 controllen: socklen_t,4261 controllen: socklen_t,
4235 pad3: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,4262 pad3: MuslOnlyPadding(.little) = 0,
4236 flags: u32,4263 flags: u32,
4237};4264};
42384265
...@@ -4276,9 +4303,9 @@ pub const cmsghdr = switch (native_os) {...@@ -4276,9 +4303,9 @@ pub const cmsghdr = switch (native_os) {
4276};4303};
42774304
4278const posix_cmsghdr = extern struct {4305const posix_cmsghdr = extern struct {
4279 pad0: if (@sizeOf(usize) == 8 and native_endian == .big) u32 else u0 = 0,4306 pad0: MuslOnlyPadding(.big) = 0,
4280 len: socklen_t,4307 len: socklen_t,
4281 pad1: if (@sizeOf(usize) == 8 and native_endian == .little) u32 else u0 = 0,4308 pad1: MuslOnlyPadding(.little) = 0,
4282 level: c_int,4309 level: c_int,
4283 type: c_int,4310 type: c_int,
4284};4311};