Skip to content

newlib: give RTEMS its own socket address types#5307

Closed
physwkim wants to merge 2 commits into
rust-lang:mainfrom
physwkim:rtems-sockaddr-len
Closed

newlib: give RTEMS its own socket address types#5307
physwkim wants to merge 2 commits into
rust-lang:mainfrom
physwkim:rtems-sockaddr-len

Conversation

@physwkim

@physwkim physwkim commented Jul 21, 2026

Copy link
Copy Markdown

On armv7-rtems-eabihf every libc socket address struct is missing the BSD
length byte, and 27 socket/file constants have the wrong value. The result:
TcpListener::bind succeeds but local_addr() fails with InvalidInput,
and every accept/peer_addr/recv_from fails the same way — std cannot
parse an address RTEMS returns. Networking is unusable on the target.

Two commits: the address structs (sockaddr, sockaddr_in, sockaddr_in6,
sockaddr_un, sockaddr_storage), then the constants (AF_INET6, PF_INET6,
SOL_SOCKET, FIONBIO, POLL*, MSG_*, O_CLOEXEC, SOCK_CLOEXEC, NCCS,
FD_SETSIZE, TCP_NODELAY, TCP_MAXSEG, IP_TTL, IP_{ADD,DROP}_MEMBERSHIP,
h_errno, AI_*, NI_*, EAI_*).

Why newlib/rtems/, not newlib/arm/

RTEMS's network stack is rtems-libbsd, imported from FreeBSD, so its socket
addresses carry a leading one-byte length. The length byte comes from the OS,
not the architecture
— plain newlib on arm has no such stack and no length
byte. src/unix/newlib/mod.rs already selects by target_os first (espidf,
horizon, vita) before falling through to target_arch, and already glob-imports
mod rtems for target_os = "rtems", so this follows the layout that is there.
newlib/arm's copies become #[cfg(not(target_os = "rtems"))], so no
non-RTEMS target changes
.

newlib/aarch64/mod.rs:22 already has sin_len — correct shape, wrong
place: it silently changes plain aarch64-none-newlib too. This PR does not
repeat that. sa_family_t = u8 is correct for RTEMS and is left alone; one
length byte plus one family byte is exactly the BSD layout.

Evidence

From the toolchain headers a shim compile actually resolves (arm-rtems6-gcc -M):

struct sockaddr    { unsigned char sa_len;  sa_family_t sa_family; char sa_data[14]; };
struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port;
                     struct in_addr sin_addr; char sin_zero[8]; };
struct sockaddr_storage { unsigned char ss_len; sa_family_t ss_family; /* ... */ };

sys/socket.h:246 has AF_INET6 28; libc says 23. _SS_MAXSIZE is 128,
where newlib/arm/mod.rs:27 declared sockaddr_storage as 28 bytes.

In the guest, getsockname on 0.0.0.0:5064 returns 10 02 00 00 …
ss_len = 16, ss_family = 2. std's socket_addr_from_c reads offset 0,
gets 16, matches neither AF_INET nor AF_INET6, and returns
InvalidInput with raw_os_error() == None — no syscall failed.

Same binary and image, libc the only variable:

before:  cannot bind CA TCP port 5064: invalid argument
after:   serving 3 records on CA port 5064 (TCP + UDP search)
         tcp local_addr = Ok(0.0.0.0:5064)
         TCP accept peer=Ok(192.168.2.127:48684) local=Ok(10.0.2.15:5064)

Scope measured by compiling, for armv7-rtems-eabihf, a generated file of
const _: () = assert!(libc::NAME == <value from arm-rtems6-gcc>);, so cfg
resolution is the compiler's and not a script's:

before after
constants wrong (of 305 measurable) 27 0
sockaddr* layout facts wrong 10 of 10 0

arm is the only reachable RTEMS outlier: aarch64 has the right shape but no
sockaddr_storage; powerpc (devkitPPC) documents having no sockaddr;
espidf/vita already carry length bytes; horizon is not BSD-derived.

Verification

  • Builds for armv7-rtems-eabihf; cargo +nightly fmt --all -- --check clean.
  • riscv32imc-esp-espidf builds, unchanged. armv7-sony-vita-newlibeabihf and
    armv6k-nintendo-3ds fail identically before and after (pre-existing
    E0573 at src/unix/mod.rs:936).
  • RTEMS 6.0.0 2faafecb, gcc 13.3.0, newlib 1b3dcfd, BSP
    xilinx_zynq_a9_qemu under qemu-system-arm.

Must reach 0.2 to unblock anyone: library/std/Cargo.toml depends on
libc 0.2.x, so a main-only fix never reaches the std that -Zbuild-std
compiles.

@rustbot label stable-nominated

Not in this PR

  • Scalar type widthstime_t, dev_t, ino_t, rlim_t, clock_t are 8
    bytes on RTEMS and declared 4; clockid_t is signed and declared unsigned.
    Separate PR (newlib: correct RTEMS scalar type widths #5308), because it overlaps the open newlib: fix definition of time_t and off_t #5132. Both PRs
    add to src/unix/newlib/rtems/mod.rs; whichever lands second needs a one-hunk
    context rebase there.
  • fcntl(F_DUPFD) fails on an rtems-libbsd socket, so TcpStream::try_clone
    cannot work there. Not a libc defectrtems-libbsd installs
    rtems_bsd_sysgen_open_error as .open_h on every socket, which
    duplicate_iop (cpukit/libcsupport/src/fcntl.c) calls. Reported separately.

physwkim added 2 commits July 22, 2026 07:04
RTEMS's networking is rtems-libbsd, a port of the FreeBSD stack, so every
socket address on RTEMS begins with a one-byte length followed by a one-byte
family, and sockaddr_storage is 128 bytes. The definitions RTEMS was picking
up came from src/unix/newlib/arm/mod.rs, which has neither.

The first cfg_if in src/unix/newlib/mod.rs selects espidf, horizon and vita by
target_os and only then falls through to target_arch, so armv7-rtems-eabihf
lands in arm/mod.rs; a second cfg_if then glob-imports mod rtems on top. The
length byte comes from libbsd, not from newlib, so the definitions belong in
the rtems module the way the other three OSes have their own -- not in the arch
module, where they would silently change any other newlib arm target.

Measured with arm-rtems6-gcc 13.3 against the RTEMS 6 headers for BSP
xilinx_zynq_a9_qemu, and cross-checked at runtime in QEMU:

  offsetof(struct sockaddr_in, sin_len)    = 0
  offsetof(struct sockaddr_in, sin_family) = 1
  sizeof(struct sockaddr_storage)          = 128
  offsetof(struct sockaddr_storage, ss_family) = 1
  sizeof(struct sockaddr_un)               = 106
  AF_INET6                                 = 28

and getsockname() on a socket bound to 0.0.0.0:5064 returns 16 bytes
starting 10 02 00 00, i.e. ss_len=16 then ss_family=2.

Consequence today: std reads the family at offset 0, sees the length, matches
neither AF_INET nor AF_INET6, and every address-returning call -- local_addr,
peer_addr, accept, recv_from, lookup_host -- fails with InvalidInput and no
OS error. TcpListener::bind succeeds and TcpListener::accept cannot work.

aarch64/mod.rs already carries the correct shape; it is cited here as evidence
of the layout, not as the model for the location.
Same cause as the previous commit: RTEMS was taking the else arm of every
cfg_if in the newlib module, which carries newlib-generic (devkitPro/Cygwin)
values, while RTEMS 6 with rtems-libbsd uses FreeBSD's.

Every value below was measured by arm-rtems6-gcc against the RTEMS 6 headers
for BSP xilinx_zynq_a9_qemu. 305 of the 349 constants the newlib modules
declare are integer constants the RTEMS headers also define; 27 of those 305
disagreed. After this commit, 0 disagree.

  POLLOUT   0x10 -> 0x4        POLLHUP        0x4 -> 0x10
  MSG_DONTWAIT 4  -> 0x80      MSG_DONTROUTE    0 -> 0x4
  MSG_WAITALL  0  -> 0x40      MSG_NOSIGNAL     0 -> 0x20000
  FIONBIO      1  -> 0x8004667e
  TCP_NODELAY 8193 -> 1        TCP_MAXSEG    8194 -> 2
  IP_TTL       8  -> 4
  IP_ADD_MEMBERSHIP  11 -> 12  IP_DROP_MEMBERSHIP 12 -> 13
  O_CLOEXEC 0x80000 -> 0x40000 SOCK_CLOEXEC O_CLOEXEC -> 0x10000000
  FD_SETSIZE 1024 -> 256       NCCS            32 -> 20
  TRY_AGAIN    4  -> 2         NO_DATA          2 -> 4
  NO_ADDRESS   2  -> NO_DATA
  AI_NUMERICSERV 0 -> 8        AI_ADDRCONFIG    0 -> 0x400
  NI_NUMERICSERV 0 -> 8        NI_DGRAM         0 -> 0x10
  EAI_FAMILY -303 -> 5         EAI_MEMORY    -304 -> 6
  EAI_NONAME -305 -> 8         EAI_SOCKTYPE  -307 -> 10

MSG_NOSIGNAL, TCP_NODELAY, POLLOUT/POLLHUP and IP_ADD_MEMBERSHIP are the ones
that break working programs rather than merely reporting the wrong number.

NCCS changes the size of struct termios. That struct is still wrong on RTEMS
for a second reason -- it is missing c_ispeed and c_ospeed -- which is left
for a separate change.
@rustbot rustbot added O-arm O-newlib O-unix S-waiting-on-review stable-nominated This PR should be considered for cherry-pick to libc's stable release branch labels Jul 21, 2026
@physwkim

Copy link
Copy Markdown
Author

@rustbot label stable-nominated

@tgross35

Copy link
Copy Markdown
Contributor

This is not in a reviewable state. Handwrite all comments, PR descriptions, commit messages, and anything else where you're communicating with humans, and ensure you are following the guidelines at https://github.com/rust-lang/libc/blob/main/CONTRIBUTING.md#submitting-a-pull-request.

@rustbot author

@tgross35
tgross35 marked this pull request as draft July 21, 2026 22:30
@rustbot

rustbot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@physwkim

Copy link
Copy Markdown
Author

This is not in a reviewable state. Handwrite all comments, PR descriptions, commit messages, and anything else where you're communicating with humans, and ensure you are following the guidelines at https://github.com/rust-lang/libc/blob/main/CONTRIBUTING.md#submitting-a-pull-request.

@rustbot author

Thanks, I'll rewrite the description and commit messages to follow the contributing guidelines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

O-arm O-newlib O-unix S-waiting-on-author stable-nominated This PR should be considered for cherry-pick to libc's stable release branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants