Modernize C builds with offline dependencies and portable runtime helpers

This commit is contained in:
Max Lv
2026-09-11 00:00:40 +08:00
parent 8fe386b5c2
commit 4455d7f8f3
110 changed files with 4295 additions and 1480 deletions
@@ -0,0 +1,42 @@
{
"commit": "8fe386b5c270fc34d2bf53ef5ffb93d93242fac2",
"platform": "Darwin arm64",
"binaries": {
"ss-local": {
"bytes": 295912,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/opt/homebrew/opt/libev/lib/libev.4.dylib (compatibility version 5.0.0, current version 5.0.0)",
"\t/opt/homebrew/opt/c-ares/lib/libcares.2.dylib (compatibility version 2.0.0, current version 2.19.5)",
"\t/usr/lib/libpcre2-8.0.dylib (compatibility version 12.0.0, current version 12.2.0)",
"\t/opt/homebrew/opt/libsodium/lib/libsodium.26.dylib (compatibility version 31.0.0, current version 31.0.0)",
"\t/opt/homebrew/opt/mbedtls@3/lib/libmbedtls.21.dylib (compatibility version 21.0.0, current version 3.6.6)",
"\t/opt/homebrew/opt/mbedtls@3/lib/libmbedcrypto.16.dylib (compatibility version 16.0.0, current version 3.6.6)"
]
},
"ss-server": {
"bytes": 314360,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/opt/homebrew/opt/libev/lib/libev.4.dylib (compatibility version 5.0.0, current version 5.0.0)",
"\t/opt/homebrew/opt/c-ares/lib/libcares.2.dylib (compatibility version 2.0.0, current version 2.19.5)",
"\t/usr/lib/libpcre2-8.0.dylib (compatibility version 12.0.0, current version 12.2.0)",
"\t/opt/homebrew/opt/libsodium/lib/libsodium.26.dylib (compatibility version 31.0.0, current version 31.0.0)",
"\t/opt/homebrew/opt/mbedtls@3/lib/libmbedtls.21.dylib (compatibility version 21.0.0, current version 3.6.6)",
"\t/opt/homebrew/opt/mbedtls@3/lib/libmbedcrypto.16.dylib (compatibility version 16.0.0, current version 3.6.6)"
]
},
"ss-tunnel": {
"bytes": 268680,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/opt/homebrew/opt/libev/lib/libev.4.dylib (compatibility version 5.0.0, current version 5.0.0)",
"\t/opt/homebrew/opt/c-ares/lib/libcares.2.dylib (compatibility version 2.0.0, current version 2.19.5)",
"\t/usr/lib/libpcre2-8.0.dylib (compatibility version 12.0.0, current version 12.2.0)",
"\t/opt/homebrew/opt/libsodium/lib/libsodium.26.dylib (compatibility version 31.0.0, current version 31.0.0)",
"\t/opt/homebrew/opt/mbedtls@3/lib/libmbedtls.21.dylib (compatibility version 21.0.0, current version 3.6.6)",
"\t/opt/homebrew/opt/mbedtls@3/lib/libmbedcrypto.16.dylib (compatibility version 16.0.0, current version 3.6.6)"
]
}
}
}
@@ -0,0 +1,24 @@
{
"commit": "8fe386b5c270fc34d2bf53ef5ffb93d93242fac2",
"platform": "Darwin arm64",
"binaries": {
"ss-local": {
"bytes": 1326328,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)"
]
},
"ss-server": {
"bytes": 1500968,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)"
]
},
"ss-tunnel": {
"bytes": 828712,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)"
]
}
}
}
@@ -0,0 +1,101 @@
[
{
"cipher": "aes-128-gcm",
"bandwidth_mbps": 7063.43292556653,
"duration_sec": 0.11876106262207031,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2656,
"server_rss_after_kb": 3472,
"tunnel_rss_before_kb": 2400,
"tunnel_rss_after_kb": 2576,
"error": null
},
{
"cipher": "aes-128-gcm",
"bandwidth_mbps": 7001.809356509003,
"duration_sec": 0.11980628967285156,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2704,
"server_rss_after_kb": 3504,
"tunnel_rss_before_kb": 2400,
"tunnel_rss_after_kb": 2576,
"error": null
},
{
"cipher": "aes-128-gcm",
"bandwidth_mbps": 6698.608105647417,
"duration_sec": 0.1252291202545166,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2688,
"server_rss_after_kb": 3488,
"tunnel_rss_before_kb": 2384,
"tunnel_rss_after_kb": 2560,
"error": null
},
{
"cipher": "aes-256-gcm",
"bandwidth_mbps": 7690.119291065227,
"duration_sec": 0.10908293724060059,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2704,
"server_rss_after_kb": 3472,
"tunnel_rss_before_kb": 2400,
"tunnel_rss_after_kb": 2576,
"error": null
},
{
"cipher": "aes-256-gcm",
"bandwidth_mbps": 7890.252575300613,
"duration_sec": 0.10631608963012695,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2672,
"server_rss_after_kb": 3488,
"tunnel_rss_before_kb": 2400,
"tunnel_rss_after_kb": 2576,
"error": null
},
{
"cipher": "aes-256-gcm",
"bandwidth_mbps": 7775.56902641376,
"duration_sec": 0.10788416862487793,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2704,
"server_rss_after_kb": 3520,
"tunnel_rss_before_kb": 2400,
"tunnel_rss_after_kb": 2576,
"error": null
},
{
"cipher": "chacha20-ietf-poly1305",
"bandwidth_mbps": 3438.7140620682108,
"duration_sec": 0.24394607543945312,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2656,
"server_rss_after_kb": 3552,
"tunnel_rss_before_kb": 2384,
"tunnel_rss_after_kb": 2592,
"error": null
},
{
"cipher": "chacha20-ietf-poly1305",
"bandwidth_mbps": 3471.04357984401,
"duration_sec": 0.24167394638061523,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2656,
"server_rss_after_kb": 3408,
"tunnel_rss_before_kb": 2368,
"tunnel_rss_after_kb": 2576,
"error": null
},
{
"cipher": "chacha20-ietf-poly1305",
"bandwidth_mbps": 3387.1740528144805,
"duration_sec": 0.24765801429748535,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2656,
"server_rss_after_kb": 3472,
"tunnel_rss_before_kb": 2384,
"tunnel_rss_after_kb": 2592,
"error": null
}
]
@@ -0,0 +1,74 @@
[
{
"cipher": "aes-128-gcm",
"trial": 0,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.25436816699948395,
"payload_mbps": 75.4811430474276
},
{
"cipher": "aes-128-gcm",
"trial": 1,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.2318156250003085,
"payload_mbps": 82.8244429165396
},
{
"cipher": "aes-128-gcm",
"trial": 2,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.24796225000136474,
"payload_mbps": 77.43114123175735
},
{
"cipher": "aes-256-gcm",
"trial": 0,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.2386028750006517,
"payload_mbps": 80.46843526067134
},
{
"cipher": "aes-256-gcm",
"trial": 1,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.2360522500002844,
"payload_mbps": 81.33792412475148
},
{
"cipher": "aes-256-gcm",
"trial": 2,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.23466012499920907,
"payload_mbps": 81.82046268007704
},
{
"cipher": "chacha20-ietf-poly1305",
"trial": 0,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.24850704099844734,
"payload_mbps": 77.26139236481416
},
{
"cipher": "chacha20-ietf-poly1305",
"trial": 1,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.24412816600124643,
"payload_mbps": 78.64721352923273
},
{
"cipher": "chacha20-ietf-poly1305",
"trial": 2,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.24165658300080395,
"payload_mbps": 79.45159102053563
}
]
@@ -0,0 +1,27 @@
{
"commit": "feature/self-contained-portable-c (working tree)",
"platform": "Darwin arm64",
"binaries": {
"ss-local": {
"bytes": 756920,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)"
]
},
"ss-server": {
"bytes": 962632,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)"
]
},
"ss-tunnel": {
"bytes": 416440,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)"
]
}
}
}
@@ -0,0 +1,101 @@
[
{
"cipher": "aes-128-gcm",
"bandwidth_mbps": 7860.532450008042,
"duration_sec": 0.10671806335449219,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2288,
"server_rss_after_kb": 3072,
"tunnel_rss_before_kb": 1872,
"tunnel_rss_after_kb": 2016,
"error": null
},
{
"cipher": "aes-128-gcm",
"bandwidth_mbps": 6582.251473027347,
"duration_sec": 0.1274428367614746,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2256,
"server_rss_after_kb": 3040,
"tunnel_rss_before_kb": 1872,
"tunnel_rss_after_kb": 2032,
"error": null
},
{
"cipher": "aes-128-gcm",
"bandwidth_mbps": 7002.575801490699,
"duration_sec": 0.11979317665100098,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2256,
"server_rss_after_kb": 3008,
"tunnel_rss_before_kb": 1872,
"tunnel_rss_after_kb": 2032,
"error": null
},
{
"cipher": "aes-256-gcm",
"bandwidth_mbps": 8336.501684349796,
"duration_sec": 0.10062503814697266,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2256,
"server_rss_after_kb": 2768,
"tunnel_rss_before_kb": 1872,
"tunnel_rss_after_kb": 2016,
"error": null
},
{
"cipher": "aes-256-gcm",
"bandwidth_mbps": 8556.573148352736,
"duration_sec": 0.0980370044708252,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2256,
"server_rss_after_kb": 2992,
"tunnel_rss_before_kb": 1872,
"tunnel_rss_after_kb": 2032,
"error": null
},
{
"cipher": "aes-256-gcm",
"bandwidth_mbps": 8498.291637500875,
"duration_sec": 0.0987093448638916,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2288,
"server_rss_after_kb": 3072,
"tunnel_rss_before_kb": 1872,
"tunnel_rss_after_kb": 2016,
"error": null
},
{
"cipher": "chacha20-ietf-poly1305",
"bandwidth_mbps": 3276.7750490181143,
"duration_sec": 0.25600194931030273,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2272,
"server_rss_after_kb": 3072,
"tunnel_rss_before_kb": 1856,
"tunnel_rss_after_kb": 2000,
"error": null
},
{
"cipher": "chacha20-ietf-poly1305",
"bandwidth_mbps": 3267.788061223197,
"duration_sec": 0.25670599937438965,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2272,
"server_rss_after_kb": 3072,
"tunnel_rss_before_kb": 1840,
"tunnel_rss_after_kb": 1984,
"error": null
},
{
"cipher": "chacha20-ietf-poly1305",
"bandwidth_mbps": 2988.673848516679,
"duration_sec": 0.28067994117736816,
"bytes_transferred": 104857600,
"server_rss_before_kb": 2272,
"server_rss_after_kb": 3040,
"tunnel_rss_before_kb": 1856,
"tunnel_rss_after_kb": 2032,
"error": null
}
]
@@ -0,0 +1,74 @@
[
{
"cipher": "aes-128-gcm",
"trial": 0,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.25596854199829977,
"payload_mbps": 75.00921734408884
},
{
"cipher": "aes-128-gcm",
"trial": 1,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.25268954199964355,
"payload_mbps": 75.98256678160065
},
{
"cipher": "aes-128-gcm",
"trial": 2,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.2656113329994696,
"payload_mbps": 72.28607222131723
},
{
"cipher": "aes-256-gcm",
"trial": 0,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.31145487499998126,
"payload_mbps": 61.6461694491093
},
{
"cipher": "aes-256-gcm",
"trial": 1,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.30316929199943843,
"payload_mbps": 63.3309523974993
},
{
"cipher": "aes-256-gcm",
"trial": 2,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.281550084000628,
"payload_mbps": 68.1939061327262
},
{
"cipher": "chacha20-ietf-poly1305",
"trial": 0,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.29268420900007186,
"payload_mbps": 65.59971262404282
},
{
"cipher": "chacha20-ietf-poly1305",
"trial": 1,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.282807709001645,
"payload_mbps": 67.89065286720428
},
{
"cipher": "chacha20-ietf-poly1305",
"trial": 2,
"packets": 2000,
"payload_bytes": 1200,
"seconds": 0.25814837500001886,
"payload_mbps": 74.37583134117578
}
]
@@ -0,0 +1,27 @@
{
"commit": "feature/self-contained-portable-c (working tree)",
"platform": "Darwin arm64",
"binaries": {
"ss-local": {
"bytes": 416424,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)"
]
},
"ss-server": {
"bytes": 622024,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)"
]
},
"ss-tunnel": {
"bytes": 397704,
"dependencies": [
"\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)",
"\t/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)"
]
}
}
}
+159
View File
@@ -0,0 +1,159 @@
# Self-contained C modernization
Status: in progress. This checklist tracks the complete agreed scope; unchecked
items are not claimed complete. Implementation branch: feature/self-contained-portable-c.
## Compatibility contract
Keep C11 and the existing CLI, JSON configuration, ss:// parsing, SIP003 plugins,
ACL blacklist/whitelist/outbound precedence, public shadowsocks.h API, legacy
stream ciphers, AEAD and SIP022 TCP/UDP behavior in the compatibility profile.
Removing regex or legacy ciphers is explicit opt-in, with configuration errors
for unavailable functionality. Linux redirection/netfilter remains Linux-only.
## Work and evidence
- [x] Baseline: fresh rebuild, unit/integration results, binary/dependency sizes,
TCP and UDP throughput, and memory measurements.
- [x] Required independent interoperability including TCP and UDP; missing peers
fail the required CI job and are reported as skips in optional local runs.
- [x] Target-scoped CMake, separate programs/static/shared library options,
reliable dependency discovery, installation/export metadata and presets.
- [ ] Offline bundled dependency mode and source release archive; explicit system
mode for distro packages; checksums, licenses, versions, update procedure.
- [x] Remove libcork: address parsing, lists, hash maps, subprocess lifecycle.
- [x] Remove libipset: bounded IPv4/IPv6 prefix storage with insertion/deletion;
differential tests including holes inside larger networks.
- [x] Vendor the small bloom filter and remove all three submodule requirements.
- [x] Portability layer for sockets/errors, I/O, clocks, randomness and processes.
- [ ] Runtime CI: Linux glibc/musl, macOS, FreeBSD, Windows MinGW.
- [x] Document MSVC/event-loop compatibility assessment as a later milestone.
- [x] Minimal profile: no regex/plugins/manager/legacy stream ciphers.
- [x] Crypto-only Mbed TLS configuration and evidence-backed provider assessment.
- [ ] Complete sanitizers/static analysis, compatibility and stress validation,
release archive offline build, installed consumer, before/after report.
## Initial evidence (2026-09-10)
Base commit: 8fe386b. Existing libcork checkout is 074e074b, different from the
superproject gitlink; it has no uncommitted files and is preserved during work.
The existing build passed all 13 unit tests. Fresh shared-dependency Release
build with the first CMake changes also passed all 13 unit tests.
The initial baseline stress/interop runs collided on a shared port; discard
those measurements and repeat serially before reporting a baseline.
## Build interface
Requires CMake 3.20+, a C11 compiler, and a build executor. Presets use Ninja;
manual configuration may use another generator.
```
cmake --preset system
cmake --build --preset system
ctest --preset system
```
Programs are in build-system/bin for either linkage mode. WITH_STATIC selects
static third-party dependencies; it does not promise a static libc or OS runtime.
SS_BUILD_EXECUTABLES, SS_BUILD_STATIC_LIBRARY and SS_BUILD_SHARED_LIBRARY control
outputs independently. Docs and platform shell tools are explicit opt-ins.
Bundled mode is the default and the `bundled` / `minimal` presets exercise it.
Maintainers stage the intended source files, then run
`scripts/source_archive.py build-artifacts/shadowsocks-libev.tar.gz`.
The archive includes tracked working-tree contents (so uncommitted changes are
included); record the final commit with a published release and use a clean
checkout for publishing. No submodules are accepted in the archive.
## Verified implementation evidence
- Bundled macOS arm64 build: 17 project tests and 14 upstream libsodium tests
pass with the crypto-only Mbed TLS configuration.
- Minimal profile: 15 project tests plus 14 upstream libsodium tests pass.
- Linux arm64 glibc: built inside Docker with networking disabled, 29 tests
passed before the later literal-rule and crypto-profile changes. Repeat the
final matrix before marking the full platform requirement complete.
- ASan + UBSan: 29 tests passed before the most recent changes; final rerun pending.
- Real shadowsocks-rust 1.24.0 interoperability passes six AEAD methods in both
directions, each with three simultaneous TCP streams (up to 1 MiB) and four
UDP payload sizes (1, 128, 1200, 4096 bytes).
- The former curl harness obeyed `no_proxy`, producing false positives by
bypassing SOCKS. Its historical results are not valid interoperability proof.
The Python replacement opens SOCKS5 sockets explicitly and reports missing
peers as skip 77, or failure under SS_REQUIRE_INTEROP=1.
- That replacement exposed truncation of 4 KiB UDP datagrams in the MTU-sized
receive buffer. The relay now receives full datagrams before authentication.
- The sanitizer baseline found null-URI output was uninitialized in ss_url_parse;
the output is now cleared before rejecting null input, with a poisoned-output
regression assertion.
- `otool -L` on bundled ss-server lists only macOS libSystem and libresolv.
- An independent CMake consumer linked and ran against the installed bundled
static library. Shared/system consumers and relocation remain to be tested.
- Existing libcork, libipset and libbloom directories are preserved locally and
ignored. Gitlinks are removed; archives no longer require those checkouts.
## Remaining completion gates
Finish Windows MinGW and FreeBSD runtime support/CI, Linux musl and x86-64,
platform socket/error/clock isolation, DNS cancellation and plugin integration
coverage, final differential IP set comparison, installation relocation and
system/shared builds, source-archive offline verification, complete local lint
and tests, and the final binary/memory/TCP/UDP before/after report. Assess MSVC
constraints explicitly. Do not infer these from the successful macOS build.
## Compatibility details
Address classification now uses inet_pton consistently with socket conversion;
malformed dotted addresses previously accepted by libcork but rejected by
inet_pton are no longer classified as IP literals. Full and suffix ACL rules
are explicit additions; normal regex remains available by default.
Legacy methods unavailable in Mbed TLS 3 (such as RC4) are not resurrected.
Crypto providers remain libsodium plus the reduced Mbed TLS primitive set:
AES-128/192-GCM, software AES fallback, MD5/SHA1 compatibility derivation, and
legacy AES/Camellia stream modes prevent a transparent libsodium-only switch.
See https://doc.libsodium.org/secret-key_cryptography/aead/aes-256-gcm and
https://shadowsocks.org/doc/sip022.html for the provider and protocol contracts.
## Final local validation and remaining runtime gates
- Fresh base-commit build (8fe386b) passes all 13 original unit tests, using the
preserved dependency checkouts described above. Both its system-shared and
static-dependency programs were rebuilt for size comparisons.
- macOS full bundled: 31 tests; minimal: 29 tests. System dependencies: 17 tests.
- ASan + UBSan: all 17 project tests and six-method TCP/UDP SIP003 tests pass.
- Clang-tidy: zero warnings/errors with Homebrew LLVM; project compiler warnings
remain errors. CI additionally uses its existing pinned clang-tidy-18 check.
- IP-set differential test: 1,028,000 comparisons against the original libipset,
including randomized full-width IPv4/IPv6 insertion/deletion and membership.
- Linux x86-64 musl: 31 unit/vendor tests and all six TCP/UDP relay cases pass
in containers with networking disabled. The preinstalled cross compiler
intermittently crashed under CPU emulation; incremental retries completed.
- Installed bundled static/shared consumers run after relocation. Installed
system-mode static/shared consumers also link and run. System static exports
require the exact Mbed TLS version used at build time to avoid an ABI mismatch.
- Windows x86-64: programs, both libraries, all tests and installed consumers
cross-compile with Zig. Native Windows and FreeBSD CI results are still pending.
- SIP003 fixture: real TCP forwarding, UDP bypass, and child cleanup pass through
the actual programs. DNS cancellation covers outstanding A/AAAA requests,
exactly-once callbacks/free callbacks and reinitialization after shutdown.
### MSVC assessment
MSVC is an explicitly deferred milestone, not a supported build today. MinGW-w64
supplies the POSIX compatibility headers/functions (`unistd.h`, `getopt`,
`ssize_t`, string helpers) used throughout the existing CLI. Native MSVC needs
those interfaces isolated, compiler-specific flags in the libsodium adapter,
libev/Win32 build verification and export/import validation. CMake now rejects
MSVC early with the supported UCRT64/Zig alternatives. Removing the event loop
or inventing replacement crypto implementations is outside this modernization.
### Crypto provider decision
Retain libsodium plus Mbed TLS crypto primitives. The bundled Mbed TLS config
omits TLS, X.509, certificate parsing and handshake machinery; it retains only
cipher/hash primitives needed by protocol compatibility. Libsodium alone cannot
replace AES-128-GCM, software AES fallback, MD5/SHA1 compatibility derivation and
legacy stream modes. PCRE2 is absent in the minimal profile. This removes three
submodule dependencies and reduces what is built without replacing audited
cryptographic algorithms with project-specific implementations.
See [measurements and tradeoffs](performance.md) for reproducible local results.
+76
View File
@@ -0,0 +1,76 @@
# Modernization measurements
Local macOS arm64 results, 2026-09-10. Base commit: `8fe386b`; comparison:
`feature/self-contained-portable-c`. Raw JSON lives in [measurements](measurements/).
These are short loopback measurements on one machine, not capacity guarantees.
## Size and runtime dependencies
Unstripped Release executable sizes in bytes:
| Program | Old system-shared | Old static dependencies | Bundled | Minimal |
|---|---:|---:|---:|---:|
| ss-local | 295,912 | 1,326,328 | 756,920 | 416,424 |
| ss-server | 314,360 | 1,500,968 | 962,632 | 622,024 |
| ss-tunnel | 268,680 | 828,712 | 416,440 | 397,704 |
Bundled programs link only macOS OS libraries (`libSystem`, and where needed
`libresolv`). They need no separately installed libev, c-ares, PCRE2, libsodium
or Mbed TLS. Compared with the old static-dependency build, ss-server shrinks
about 36%; minimal shrinks about 59%. Compared with a shared-dependency binary
alone, bundling makes the executable larger; that comparison excludes the old
libraries' disk footprint. Bundled source archives add roughly 12 MB to checkout
and release size. There is no configure-time network access or submodule fetch.
## TCP and resident memory
Median of three 100 MiB transfers using `tests/stress_test.py`, old shared
dependencies versus bundled. Both sets pass all nine transfers.
| Cipher | Before Mbps | Bundled Mbps | Before server RSS after, KiB | Bundled server RSS after, KiB |
|---|---:|---:|---:|---:|
| AES-128-GCM | 7,002 | 7,003 | 3,488 | 3,040 |
| AES-256-GCM | 7,776 | 8,498 | 3,488 | 2,992 |
| ChaCha20-IETF-Poly1305 | 3,439 | 3,268 | 3,472 | 3,072 |
Bundled tunnel RSS after transfer was about 2,000–2,032 KiB versus
2,576–2,592 KiB before. RSS includes OS accounting and shared-page effects;
this is not a proof of bounded memory under arbitrary workloads. Transfer times
are short (roughly 0.1–0.3 s), so the apparent AES-256 improvement and ChaCha
regression need longer controlled measurements before drawing performance claims.
## UDP
Median of three runs, 2,000 sequential 1,200-byte echo packets per run through
SOCKS5 UDP ASSOCIATE. Every packet's payload and relay source port are checked.
| Cipher | Before payload Mbps | Bundled payload Mbps |
|---|---:|---:|
| AES-128-GCM | 77.43 | 75.01 |
| AES-256-GCM | 81.34 | 63.33 |
| ChaCha20-IETF-Poly1305 | 78.65 | 67.89 |
This round-trip benchmark includes Python thread creation, scheduling and SOCKS
overhead. It shows lower observed UDP rates in this run, up to 22%; modernization
is not claimed to improve UDP throughput. The portable libsodium backend is a
maintenance/performance tradeoff. Optimized backends require separate compiler
probes and cross-platform known-answer tests before adoption.
Independently of these timings, the new interoperability test exposed the old
MTU-sized receive buffer truncating 4 KiB UDP payloads. Full datagram reception
now passes 1, 128, 1,200 and 4,096-byte interoperability cases against
shadowsocks-rust in both directions.
## Reproduce
```sh
python3 tests/stress_test.py --bin build-bundled/bin --size 100 --repeat 3 --json tcp.json
python3 tests/measure_udp.py --bin build-bundled/bin --output udp.json
python3 tests/interop.py --bin build-bundled/bin
python3 tests/interop.py --self --bin build-bundled/bin --plugin tests/sip003_fixture.py
```
Run comparisons serially. The baseline was rebuilt from its own worktree using
the preserved original dependency checkouts; no modernization fixes were applied
to that source. The historical curl interop harness obeyed `no_proxy` and could
bypass SOCKS, so its old results are excluded from compatibility evidence.