Skip to content

feat(libc): implement getifaddrs and freeifaddrs - #1772

Merged
oneandonlydean merged 4 commits into
boykopovar:mainfrom
LuisBiceps:feat/libc-ifaddrs
Oct 10, 2026
Merged

oneandonlydean merged 4 commits into
boykopovar:mainfrom
LuisBiceps:feat/libc-ifaddrs

Conversation

@LuisBiceps

@LuisBiceps LuisBiceps commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What

Implements getifaddrs (NID j-gWL6wDros) and freeifaddrs (NID N2OjwJJGjeQ) in libc. 16 of the homebrew titles in the #1578 audit import both from libSceLibcInternal, which resolves through libc.

  • One entry per host IPv4 or IPv6 address, in FreeBSD's 56-byte struct ifaddrs with FreeBSD sockaddr_in / sockaddr_in6 (length byte, AF_INET 2, AF_INET6 28). Every entry has a netmask. Broadcast interfaces get the IPv4 broadcast address, point-to-point interfaces the destination address. Flags use FreeBSD's values (IFF_MULTICAST is 0x8000, not Linux's 0x1000).
  • Linux reads the host's getifaddrs. Windows reads GetAdaptersAddresses: names from ConvertInterfaceLuidToNameA (NDIS names such as loopback_0), netmask from OnLinkPrefixLength, broadcast from the address and netmask.
  • As on FreeBSD, the list is one allocation, so freeifaddrs is a single free.
  • Errors return -1 with FreeBSD errno, as getcwd here does: a null list pointer gives EFAULT, and host or allocation failures give ENOMEM, ENOBUFS, EMFILE or ENFILE. Any other host failure throws.
  • Linux edge cases: a point-to-point address without a peer loses IFF_POINTOPOINT, and an IPv4 broadcast address the host omits is computed from the netmask, so a flag always comes with its address, as on FreeBSD.
  • Windows edge cases:
    • The flags are only IFF_UP, IFF_RUNNING, IFF_LOOPBACK, IFF_BROADCAST and IFF_MULTICAST, derived from the adapter.
    • A prefix length the host reports as unspecified (255) gets a host mask.
    • PPP and tunnel adapters get neither IFF_BROADCAST nor IFF_POINTOPOINT, since their peer address is not reported and FreeBSD always fills ifa_dstaddr with IFF_POINTOPOINT.
    • An NDIS name longer than 15 bytes (FreeBSD's IFNAMSIZ is 16) becomes if<index>.
    • The adapter query is retried at most 4 times.
  • Recorded in TechnicalDebt:
    • FreeBSD's per-interface AF_LINK entries and their if_data are not produced, so interfaces without an address are omitted.
    • Names are the host's.
    • The Windows flag and prefix rules above apply.

Tested

  • New guest_interfaces test, registered in ctest:
    • Host comparison: compares every guest entry, in order, with a snapshot from the same host API (getifaddrs on Linux, GetAdaptersAddresses on Windows). Family, address, netmask and IPv6 scope id must match, with up to 3 attempts in case DHCP or a VPN changes an address between the two snapshots.
    • Per entry: name length, FreeBSD-only flag bits, sockaddr layouts (zero port, flowinfo and padding), and 8-byte alignment. A broadcast or point-to-point flag must come with a destination address.
    • Loopback: requires 127.0.0.1/8 with IFF_UP | IFF_LOOPBACK and no IFF_BROADCAST.
    • Errors: getifaddrs(nullptr) must return -1 with EFAULT, and freeifaddrs(nullptr) must return.
  • The test fails against 16 deliberate breakages, among them:
    • flag translation (Linux's IFF_MULTICAST leaking through, the loopback flag dropped, loopback marked broadcast);
    • sockaddr length and family bytes, and the netmask (taken from the address, or a prefix mask off by one bit);
    • dropped IPv4 or IPv6 entries, a truncated list, a dropped IPv6 scope id;
    • a broadcast flag without a broadcast address, a non-zero port or flowinfo, and the null-pointer errno.
  • Windows 10, MinGW-w64 GCC 15.2.0 (WinLibs 15.2.0posix-14.0.0-ucrt-r7), Release, on main c963432: guest_interfaces passes (--repeat until-fail:3), and so does the full suite. The NID-patched libc.prx exports both NIDs.
  • Ubuntu 26.04.1 (WSL2), GCC 15.2.0, Release, ANYPS5_ENABLE_SPIRV_TOOLS=ON, on main c963432: guest_interfaces passes (--repeat until-fail:3), and so does the full suite.
  • tools/check_test_registration.py and tools/check_conventions.py pass.
  • PSBox Cloud Gaming (PPSA99810) 0.7.0 from the homebrew catalog and its 0.8.0 release, and Cloud Play (PPSA99600) 1.0.0, relinked with --windows (bundled libc.prx unwrapped) and run on Windows 10:
  • @babyhuey's runs on main 197ffac, after fix(relinker): reject guest modules in SELF containers #2605, with each title's own libc.prx loaded, on Linux (Ubuntu 24.04) and Windows 11: four catalog titles stop at these functions, and all four get past them with this PR.
Title main with this PR
PS5MC (PPSA99420) j-gWL6wDros (getifaddrs) 0NJDcQ2B8vE (execvp)
Cloud Play (PPSA99600) j-gWL6wDros (getifaddrs) 04AjkP0jO9U (_umtx_op, #1482)
PICO-8 for PS5 (PPSA99808) N2OjwJJGjeQ (freeifaddrs) 5G2ONUzRgjY (memrchr)
PSBox Cloud Gaming (PPSA99810) j-gWL6wDros (getifaddrs) oxMp8uPqa+U (pthread_set_name_np)

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

@SleglJan

SleglJan commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Linux run (the description lists Windows and WSL2): CachyOS, kernel 7.2.9, GCC 16.2.1, Release with BUILD_TESTING=ON and ANYPS5_ENABLE_SPIRV_TOOLS=ON. This PR's head def3466 merged onto main b858fdc, which merges cleanly.

  • guest_interfaces aborts in 3 of 3 runs here, at the line 140 check, if (item->flags & 0x10) Require(item->destination != nullptr) (found with a local build that prints the failing Require's line). The host has a TUN interface with IFF_POINTOPOINT, one IPv4 address and two IPv6 addresses; the host getifaddrs gives an ifa_dstaddr for the IPv4 address and none for the IPv6 ones, so the implementation leaves those two destinations null and the test rejects them. The previous head d074b21, whose test has no such check, passed --repeat until-fail:3 on the same host; the Linux destination code is the same in both heads. Host interfaces: lo, two Ethernet links with IPv4 and IPv6 addresses, a wireless interface that is down, and the tunnel.
  • The patched libc.prx exports j-gWL6wDros and N2OjwJJGjeQ.
  • tools/check_conventions.py --base upstream/main and tools/check_test_registration.py pass.
  • tools/import_audit.py on five of the 16 titles from the Four open-source homebrew titles stop at start-up on unresolved imports (sysctl, pthread_set_name_np, isatty, mkstemp, signal sets, Opus decoders) #1578 table, absent imports on main b858fdc → with this PR: PPSA17641 SymphonyStation5 35 → 33, PPSA97510 PSXS5 72 → 70, PPSA99009 ProsperoLichess 37 → 35, PPSA99082 OpenNOW PS5 - Prototype 36 → 34, PPSA99808 PICO-8 for PS5 28 → 26. In each case the two that disappear are getifaddrs and freeifaddrs; no title was run, since each still has other absent imports.

AI-assisted: yes (Claude Code).

@LuisBiceps

Copy link
Copy Markdown
Contributor Author

Thanks for testing on a TUN host. That failure is fixed in the current head, 4f10bd0, pushed after your run. An address on a point-to-point interface without a peer of the same family now loses IFF_POINTOPOINT, as on FreeBSD, where the flag means ifa_dstaddr is filled. I reproduced your layout on Ubuntu 26.04 (WSL2): a tun interface with one IPv4 address with a peer and two IPv6 addresses without one. def3466 aborts there, and 4f10bd0 passes --repeat until-fail:3. It also passes with an IPv4 address without a peer, an IPv6 address with a peer, and a dummy interface with /32 and /128 addresses.

@LuisBiceps

Copy link
Copy Markdown
Contributor Author

A title reaches getifaddrs once its current stop is resolved. Cloud Play (PPSA99600) 1.0.0, from the homebrew catalog, relinked with --windows on main f145139 and run on Windows 10, stops at:

Build Stop
main FAIL: unresolved ELF import 6kPnj51T62Y from libSceVideoOut.prx (sceVideoOutGetResolutionStatus, #1714)
main + #1714 FAIL: unresolved ELF import j-gWL6wDros from libSceLibcInternal.prx (getifaddrs)
main + #1714 + this PR FAIL: unresolved ELF import e-YMQ+2tj9M from libSceAgcDriver.prx (sceAgcDriverGetTFRing, #1978)
main + #1714 + this PR + #1978 FAIL: unresolved ELF import Jc6E7N+dHz0 from libkernel.prx (system, #1859)

@CouchRiv

CouchRiv commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

A title that stops first on this: PSBox Cloud Gaming (PPSA99810, v0.8.0 from the homebrew catalog), relinked for Linux and run on main d70b899 (CachyOS, GCC, Release, in a bubblewrap sandbox):

symbol lookup error: app.elf: undefined symbol: j-gWL6wDros

j-gWL6wDros is getifaddrs, the first unresolved import it binds. Not run with this PR.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Progress report (3d1a7d9 → 63a20d6)

📈 System libraries: 85.76% (+0.01%, +2 implemented functions)

✅ 2 implemented
Library Name
libc freeifaddrs_nid_postfix
libc getifaddrs_nid_postfix

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 9, 2026
@LuisBiceps

Copy link
Copy Markdown
Contributor Author

Thanks @CouchRiv. It reproduces on Windows, and with this PR PSBox Cloud Gaming moves on to the next import. Version 0.7.0 is the catalog's and 0.8.0 is its newest release. Both, relinked with --windows and run on Windows 10, stop at the same imports:

Build Stop
main d70b899 FAIL: unresolved ELF import j-gWL6wDros from libSceLibcInternal.prx (getifaddrs)
main d70b899 + this PR FAIL: unresolved ELF import JGfTMBOdUJo from libkernel.prx (sceKernelGetFsSandboxRandomWord, #1807)

The branch was conflicting in TechnicalDebt, so I merged main into it; the commits already reviewed are unchanged. A commit before the merge moves the guest_interfaces test and the TechnicalDebt entry to lines no other open pull request changes, so the merge needed no hand edits and later merges shouldn't conflict with this one. guest_interfaces passes --repeat until-fail:3 on the merged head on Windows 10 and Ubuntu 26.04, and so do the full suites.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Please rebase onto main and force-push instead of merging main into the branch (f4e507f); merge commits aren't taken here.

List the host's IPv4 and IPv6 addresses in FreeBSD's struct ifaddrs layout, with FreeBSD sockaddrs and interface flags, from getifaddrs on Linux and GetAdaptersAddresses on Windows. The list is one allocation, so freeifaddrs is a single free.
… edge cases

Return -1 with the FreeBSD errno for a null list and for expected host and allocation failures, as getcwd does. On Windows, give an unspecified prefix length a host mask, leave PPP and tunnel adapters without broadcast or point-to-point flags since their peer address is not reported, fall back to if<index> for long NDIS names, and bound the adapter query retries. Compare every entry with the host's own list in the test.
…Linux

Drop IFF_POINTOPOINT from an address without a peer and compute an IPv4 broadcast address the host omits from the netmask, since FreeBSD always provides the address when the flag is set. Check zero port, flowinfo and padding in the test.
@adriadam10

Copy link
Copy Markdown
Collaborator

Rebased onto current main as requested: the merge commit is gone and the four commits are replayed in order. Conflicts were only adjacency in core/libs/prx/libc/CMakeLists.txt and docs/dev/TechnicalDebt.md; the test block and the TechnicalDebt line sit where your last commit put them. Source and test files are unchanged.

Checked on Linux: scoped build and ctest (58 tests) green, guest_interfaces passes; merging once CI is green.

… from lines other pull requests change

The guest_interfaces test now follows guest_thread_callback_abi instead of guest_errors, and the TechnicalDebt entry follows the libc SSE4a trap emulation entry instead of ending the Functional list, where other open pull requests add theirs.
@LuisBiceps

Copy link
Copy Markdown
Contributor Author

Thanks for rebasing it. The replayed last commit added the getifaddrs TechnicalDebt entry twice (lines 369 and 370 of dff45a0), so I force-pushed 63a20d6: the same four commits, with the second copy dropped from the last one. Nothing else changed, and it still merges cleanly with main 919c49b.

The write-watch (macos-15-intel) failure on dff45a0 was a write_watch_threads timeout. That job builds only WriteWatch.cpp and GuestWriteWatch.cpp, which this PR doesn't touch.

@klosixgaming-arch

Copy link
Copy Markdown
Contributor

Independent validation on Windows

Built and ran the PR head 63a20d67 on my own box:

  • Windows 11 Pro (26200), WinLibs MinGW-w64 GCC 15.2.0 (r7), Release, BUILD_TESTING=ON.

Results:

  • guest_interface_tests PASSES — 3 consecutive runs (exit 0). It compares every guest entry (in order) against a live GetAdaptersAddresses host snapshot, checks the FreeBSD ifaddrs/sockaddr layout + 8-byte alignment, the loopback 127.0.0.1/8 with IFF_UP|IFF_LOOPBACK and no IFF_BROADCAST, and the null-pointer EFAULT/freeifaddrs(nullptr) contract.
  • The build-system change (libc now links ws2_32 iphlpapi on Windows) compiles and links cleanly.
  • The TechnicalDebt.md entry honestly documents the limits (AF_LINK/if_data not produced, host names, Windows flag rules) — good.

Rigor check (is the test real, or a pass-through?): I deliberately broke the Windows flag translation — changed IF_TYPE_SOFTWARE_LOOPBACK → flags |= guestLoopback to flags |= guestUp — rebuilt, and the test aborted (FATAL: abort() (SIGABRT) from Require(matched)), because the loopback assertion no longer held. Restoring the line → PASS (3×). So the test genuinely exercises the host↔guest comparison and flag mapping rather than passing unconditionally.

Honest limits of what I verified:

  • I only exercised the Windows GetAdaptersAddresses path (my host). I could not verify the Linux/POSIX getifaddrs path or the Linux-only edge cases (point-to-point address without a peer losing IFF_POINTOPOINT, IPv4 broadcast computed from the netmask, IFF_NOARP/PROMISC/ALLMULTI), since I have no Linux here.
  • I did not run the homebrew titles (PSBox / Cloud Play) — no game/ROM on this machine.

Written with AI assistance.

@babyhuey

Copy link
Copy Markdown
Contributor

Title check on current main, since the title stops moved after #2605. 63a20d6 merged onto main 197ffac (clean), Linux (Ubuntu 24.04, GCC 13) and Windows 11 (MinGW-w64 GCC 15.2 from WinLibs), Release.

Title main with this PR
PS5MC (PPSA99420) j-gWL6wDros (getifaddrs) 0NJDcQ2B8vE (execvp)
Cloud Play (PPSA99600) j-gWL6wDros (getifaddrs) 04AjkP0jO9U (_umtx_op, #1482)
PICO-8 for PS5 (PPSA99808) N2OjwJJGjeQ (freeifaddrs) 5G2ONUzRgjY (memrchr)
PSBox Cloud Gaming (PPSA99810) j-gWL6wDros (getifaddrs) oxMp8uPqa+U (pthread_set_name_np)

Example lines for PS5MC: main Windows FAIL: unresolved ELF import j-gWL6wDros (getifaddrs) from libSceLibcInternal.prx; with this PR, Linux app.elf: symbol lookup error: app.elf: undefined symbol: 0NJDcQ2B8vE, Windows FAIL: unresolved ELF import 0NJDcQ2B8vE (execvp) from libkernel.prx.

@Get-fix Get-fix mentioned this pull request Oct 10, 2026
9 tasks done
@oneandonlydean
oneandonlydean merged commit cf5d9bc into boykopovar:main Oct 10, 2026
15 checks passed
@Amirtheshwaran Amirtheshwaran mentioned this pull request Oct 10, 2026
6 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants