6565 cmake -GNinja -B build-small -DCMAKE_BUILD_TYPE=Release \
6666 -DBUILD_SHARED_LIBS=0 -DBUILD_TESTING=OFF -DOPENSSL_SMALL=ON
6767 cmake --build build-small --target crypto
68+ - name : Build libcrypto.a (OPENSSL_SMALL, section-GC)
69+ # Same as build-small but compiled with -ffunction-sections
70+ # -fdata-sections, so --gc-sections can strip at function granularity
71+ # inside bcm.c.o. This mirrors how aws-lc-rs (CcBuilder) compiles the
72+ # library, and is the configuration where anchor-driven retention is
73+ # measurable: without it, any reference into bcm.c.o retains the whole
74+ # object and masks what the anchors actually pin.
75+ run : |
76+ cmake -GNinja -B build-small-gc -DCMAKE_BUILD_TYPE=Release \
77+ -DBUILD_SHARED_LIBS=0 -DBUILD_TESTING=OFF -DOPENSSL_SMALL=ON \
78+ -DCMAKE_C_FLAGS="-ffunction-sections -fdata-sections" \
79+ -DCMAKE_CXX_FLAGS="-ffunction-sections -fdata-sections"
80+ cmake --build build-small-gc --target crypto
6881 - name : Link consumer against each libcrypto and compare
6982 env :
7083 EXPECTED_REDUCTION_PERCENT : ${{ matrix.expected_reduction_percent }}
7891 ./consumer-small
7992 size_default=$(stat -c %s consumer-default)
8093 size_small=$(stat -c %s consumer-small)
94+ archive_default=$(stat -c %s build-default/crypto/libcrypto.a)
95+ archive_small=$(stat -c %s build-small/crypto/libcrypto.a)
8196 if [ "$size_default" -le 0 ]; then
8297 echo "ERROR: default consumer binary has zero size"
8398 exit 1
@@ -86,18 +101,64 @@ jobs:
86101 {
87102 echo "## OPENSSL_SMALL static-link size (${{ matrix.name }})"
88103 echo ""
89- echo "| Build | Consumer binary |"
90- echo "|-------|-----------------|"
91- echo "| Default | $(numfmt --to=iec "$size_default") (${size_default} B) |"
92- echo "| OPENSSL_SMALL | $(numfmt --to=iec "$size_small") (${size_small} B) |"
93- echo "| **Reduction** | **${reduction}%** |"
104+ echo "| Build | libcrypto.a | Consumer binary |"
105+ echo "|-------|-------------|------------- ----|"
106+ echo "| Default | $(numfmt --to=iec "$archive_default") | $(numfmt --to=iec "$ size_default") (${size_default} B) |"
107+ echo "| OPENSSL_SMALL | $(numfmt --to=iec "$archive_small") | $(numfmt --to=iec "$ size_small") (${size_small} B) |"
108+ echo "| **Reduction** | | **${reduction}%** |"
94109 } >> "$GITHUB_STEP_SUMMARY"
95110 echo "default=${size_default} small=${size_small} reduction=${reduction}%"
96111 if [ "$reduction" -lt "$EXPECTED_REDUCTION_PERCENT" ]; then
97112 echo "FAIL: expected >= ${EXPECTED_REDUCTION_PERCENT}% reduction, got ${reduction}%"
98113 exit 1
99114 fi
100115 echo "PASS: ${reduction}% reduction meets the ${EXPECTED_REDUCTION_PERCENT}% threshold"
116+ - name : Measure GC-enabled consumer and EVP key-parsing anchor
117+ # Intentionally single-toolchain (clang + GNU ld on Linux, like the
118+ # whole job): the matrix legs vary only by architecture, so the
119+ # reported numbers are per-arch, not a gcc cross-check. The anchor
120+ # cost is a reachability property and ~compiler-independent anyway.
121+ run : |
122+ set -euo pipefail
123+ # Link against the section-GC library build (the aws-lc-rs-like
124+ # configuration). The EVP-parse variant adds one
125+ # EVP_parse_public_key call, which anchors asn1_evp_pkey_methods[]
126+ # (crypto/evp_extra/p_methods.c) and with it every key type's ASN.1
127+ # machinery -- currently including all of ML-KEM/ML-DSA -- even
128+ # under full section GC. The delta between the two binaries
129+ # quantifies what key-parsing consumers inherit. Advisory only; not
130+ # thresholded (yet).
131+ common="-O2 -I include -ffunction-sections -fdata-sections"
132+ libs="-lpthread -ldl -lm -Wl,--gc-sections"
133+ clang $common tests/binary-size/main.c build-small-gc/crypto/libcrypto.a $libs -o consumer-gc
134+ clang $common -DAWSLC_SIZE_CHECK_EVP_PARSE tests/binary-size/main.c \
135+ build-small-gc/crypto/libcrypto.a $libs -o consumer-gc-evp
136+ echo "Sanity: run the GC-enabled consumers"
137+ ./consumer-gc
138+ ./consumer-gc-evp
139+ size_gc=$(stat -c %s consumer-gc)
140+ size_gc_evp=$(stat -c %s consumer-gc-evp)
141+ evp_overhead=$(( size_gc_evp - size_gc ))
142+ {
143+ echo "### GC-enabled consumer (OPENSSL_SMALL + section GC, ${{ matrix.name }})"
144+ echo ""
145+ echo "| Consumer | Binary size |"
146+ echo "|----------|-------------|"
147+ echo "| base | $(numfmt --to=iec "$size_gc") (${size_gc} B) |"
148+ echo "| base + \`EVP_parse_public_key\` | $(numfmt --to=iec "$size_gc_evp") (${size_gc_evp} B) |"
149+ echo "| **EVP key-parsing anchor cost** | **$(numfmt --to=iec "$evp_overhead") (${evp_overhead} B)** |"
150+ } >> "$GITHUB_STEP_SUMMARY"
151+ echo "gc=${size_gc} gc_evp=${size_gc_evp} evp_overhead=${evp_overhead}"
152+ for bin in consumer-gc consumer-gc-evp; do
153+ {
154+ echo "<details><summary>Largest retained symbols: ${bin}</summary>"
155+ echo ""
156+ echo '```'
157+ nm -S --size-sort --radix=d "$bin" | tail -25 | tac
158+ echo '```'
159+ echo "</details>"
160+ } >> "$GITHUB_STEP_SUMMARY"
161+ done
101162
102163 small-tests :
103164 if : github.repository_owner == 'aws'
0 commit comments