Skip to content

Commit 032ab6c

Browse files
authored
fix(hazmat-kzg): build LeanHazmatKzg on macOS (etheorem#13)
* fix(macos-kzg-build): use Mach-O section name for embedded trusted setup on Apple * fix(macos-kzg-build): use platform-specific KZG link and symbol names * fix(macos-kzg-build): ignore local clang output * fix(macos-kzg-build): describe the platform-specific blst link flag * fix(macos-build): make EthCLLib/EthCLSpecs libcrypto link portable EthCLLib and EthCLSpecs re-supply the system libcrypto flag that Lake doesn't propagate across `require` (see SizzLean's lakefile note), but they linked it as `-l:libcrypto.so.3`, an exact-SONAME form that only resolves on Linux. Switch to `-lcrypto`, which resolves through the unversioned symlink every platform's `-dev`/Homebrew OpenSSL package provides, and add the Homebrew `opt` paths (Apple Silicon and Intel) to the existing Linux `-L` candidate list. The linker already ignores whichever `-L` paths are absent, so one flag list now covers all three platforms. Verified with `lake build EthCLSpecs` (clean, 365/365 jobs) and `just ethcl-test`, whose native_decide self-tests (BLS sign/verify, KZG cell-proof-batch round-trips through the FFI crypto backend) exercise the linked shared libraries at runtime, not just at link time. Append a dated addendum to hazmat-docs/PLAN.md's Stage 0 outcome note, which recorded `-l:libcrypto.so.3` as the settled flag; the propagation finding and the decision to re-supply independently are unchanged, only the literal flag. * docs(contributing): document macOS native deps in the Quick Start The Quick Start only listed `apt-get install libssl-dev` and referenced the Linux-only `libcrypto.so.3`, even though README.md's "Native dependencies" section already documents the macOS Homebrew path. Add the macOS line, add the missing `pkg-config` to the Linux line (already present in README.md's fuller version), and point to README.md for the per-platform details instead of naming one platform's library file.
1 parent b7e7dfe commit 032ab6c

9 files changed

Lines changed: 76 additions & 30 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ packages/SizzLean/bench/*.tsv
5858

5959
# macOS Finder cruft
6060
.DS_Store
61+
a.out

CONTRIBUTING.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ correctness on the verified core. Contributions are welcome.
1111
# 1. Install elan (one-time). https://elan.lean-lang.org/
1212
curl https://elan.lean-lang.org/elan-init.sh -sSf | sh
1313

14-
# 2. Install native deps. On Debian/Ubuntu:
15-
sudo apt-get install libssl-dev
14+
# 2. Install native deps.
15+
# Linux (Debian/Ubuntu):
16+
sudo apt-get install libssl-dev pkg-config
17+
# macOS:
18+
brew install openssl@3 pkg-config
1619

1720
# 3. Clone and build.
1821
git clone https://github.com/etheorem/etheorem
@@ -22,7 +25,9 @@ lake build
2225

2326
The pinned toolchain in `lean-toolchain` is picked up by elan
2427
automatically. First build pulls Lake deps + compiles the FFI
25-
SHA-256 shim against `libcrypto.so.3`.
28+
SHA-256 shim against the system OpenSSL `libcrypto` (see
29+
[`README.md`](README.md#native-dependencies) for the per-platform
30+
library names and discovery details).
2631

2732
## Running the test suites
2833

hazmat-docs/PLAN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ discovery, accepted, per the no-shared-lakefile-code decision
7373
> `-l:libcrypto.so.3`, and `LeanHazmatSha256` keeps its own for its
7474
> test lib. No package reverts to a TOML lakefile. This matches the
7575
> prior link-arg evidence and the no-shared-lakefile-code decision (§3.3).
76+
>
77+
> **Addendum (macOS portability).** `EthCLLib`/`EthCLSpecs`'s hardcoded
78+
> flag later changed from the Linux-only `-l:libcrypto.so.3` to the
79+
> portable `-lcrypto` plus Homebrew's `-L` paths, so the same
80+
> independent, hardcoded re-supply this stage decided on now resolves
81+
> on macOS too. The propagation finding and the decision to re-supply
82+
> independently are unchanged, only the literal flag each package hardcodes.
7683
7784
---
7885

packages/EthCLLib/lakefile.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ defaultTargets = ["EthCLLib"]
66
# `libcrypto` flag its `native_decide` self-tests need transitively through
77
# SizzLean's FFI SHA-256. The blst / c-kzg archives propagate automatically
88
# as `extern_lib`s, only the system shared lib needs re-stating here.
9-
# Two `-L` paths cover the common Linux libdir layouts (Debian multiarch and
10-
# `/usr/lib64` on Gentoo / Fedora / RHEL); the linker ignores whichever is absent.
11-
moreLinkArgs = ["-L/usr/lib/x86_64-linux-gnu", "-L/usr/lib64", "-l:libcrypto.so.3"]
9+
# The `-L` paths cover the common libdir layouts across platforms (Debian
10+
# multiarch, `/usr/lib64` on Fedora/RHEL, and Homebrew's `opt` symlink on
11+
# both macOS architectures); the linker ignores whichever are absent.
12+
# `-lcrypto` (not `-l:libcrypto.so.3`) resolves via the unversioned symlink
13+
# every platform's `-dev`/Homebrew package provides, so it works on both.
14+
moreLinkArgs = ["-L/usr/lib/x86_64-linux-gnu", "-L/usr/lib64", "-L/opt/homebrew/opt/openssl@3/lib", "-L/usr/local/opt/openssl@3/lib", "-lcrypto"]
1215
moreLeancArgs = ["-march=native"]
1316

1417
# SizzLean supplies the SSZ machinery (SSZRepr, Box, the cache, sszGet /

packages/EthCLSpecs/lakefile.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ defaultTargets = ["EthCLSpecs"]
33

44
# Re-supply the system libcrypto flag (does not propagate across `require`); the
55
# blst / c-kzg archives propagate transitively from the LeanHazmat packages.
6-
# Two `-L` paths cover the common Linux libdir layouts (Debian multiarch and
7-
# `/usr/lib64` on Gentoo / Fedora / RHEL); the linker ignores whichever is absent.
8-
moreLinkArgs = ["-L/usr/lib/x86_64-linux-gnu", "-L/usr/lib64", "-l:libcrypto.so.3"]
6+
# The `-L` paths cover the common libdir layouts across platforms (Debian
7+
# multiarch, `/usr/lib64` on Fedora/RHEL, and Homebrew's `opt` symlink on
8+
# both macOS architectures); the linker ignores whichever are absent.
9+
# `-lcrypto` (not `-l:libcrypto.so.3`) resolves via the unversioned symlink
10+
# every platform's `-dev`/Homebrew package provides, so it works on both.
11+
moreLinkArgs = ["-L/usr/lib/x86_64-linux-gnu", "-L/usr/lib64", "-L/opt/homebrew/opt/openssl@3/lib", "-L/usr/local/opt/openssl@3/lib", "-lcrypto"]
912
moreLeancArgs = ["-march=native"]
1013

1114
# The framework. Pulls in SizzLean (SSZ), LeanHazmatBls / LeanHazmatKzg (crypto)

packages/LeanHazmatKzg/LeanHazmatKzg/Ffi.lean

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
-- Build-ordering import (not for any Lean symbol): this package's
2-
-- precompiled `.so` links Bls's shared lib (`moreLinkArgs`,
3-
-- `-l:libleanhazmat_bls.so`) for the `blst_*` symbols c-kzg references.
2+
-- precompiled `.so` links Bls's shared lib (`moreLinkArgs`'s
3+
-- platform-specific `-l`) for the `blst_*` symbols c-kzg references.
44
-- Importing the Bls module makes the Bls package, and so its shared
55
-- lib, a build prerequisite of this module, ordering it before this
66
-- package's link step (otherwise a clean parallel build can race and the
7-
-- KZG `.so` link fails with "unable to find -l:libleanhazmat_bls.so").
7+
-- KZG `.so` link fails with "unable to find" the missing lib).
88
import LeanHazmatBls
99

1010
/-!

packages/LeanHazmatKzg/csrc/trusted_setup_incbin.S

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,29 @@
1414
// `just hazmat-kzg-vendor`). The C shim reads these bytes via `fmemopen` +
1515
// `load_trusted_setup_file` once at library load.
1616

17+
#ifdef __APPLE__
18+
.section __DATA,__const
19+
#else
1720
.section .rodata
21+
#endif
22+
23+
// Mach-O's C ABI prefixes every C symbol with `_` at the linker level;
24+
// the compiler adds it automatically when it compiles `kzg_shim.c`'s
25+
// `extern const char lean_hazmat_kzg_ts_start[]`, but an `.S` file
26+
// bypasses that mangling, so the label written here must carry the
27+
// `_` itself on Apple to match what the linker looks for.
28+
#ifdef __APPLE__
29+
.global _lean_hazmat_kzg_ts_start
30+
_lean_hazmat_kzg_ts_start:
31+
#else
1832
.global lean_hazmat_kzg_ts_start
1933
lean_hazmat_kzg_ts_start:
34+
#endif
2035
.incbin TRUSTED_SETUP_PATH
36+
#ifdef __APPLE__
37+
.global _lean_hazmat_kzg_ts_end
38+
_lean_hazmat_kzg_ts_end:
39+
#else
2140
.global lean_hazmat_kzg_ts_end
2241
lean_hazmat_kzg_ts_end:
42+
#endif

packages/LeanHazmatKzg/docs/ARCHITECTURE.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ work without duplicating it:
5858
one blst copy and no duplicate symbols.
5959
* **Shared lib (precompiled module / `native_decide`).** The precompiled
6060
module's `.so` would otherwise have undefined `blst_*` at load. The
61-
package's `moreLinkArgs` give it `-l:libleanhazmat_bls.so` +
62-
`-L`/`-rpath` into Bls's build lib, so the loader pulls Bls's shared lib
63-
and resolves `blst_*`, mirroring how `LeanHazmatSha256`'s `.so` gains
64-
`NEEDED libcrypto.so.3`. That link reference is invisible to Lake's
65-
scheduler, so the `extern_lib` folds Bls's shared-lib build into its own
66-
dependency trace (`Job.zipWith` over `findExternLib? `libleanhazmat_bls`)
67-
to keep clean parallel builds from racing ahead of Bls's `.so`.
61+
package's `moreLinkArgs` give it `-L`/`-rpath` into Bls's build lib plus
62+
a platform-appropriate `-l`: `-l:libleanhazmat_bls.so` (a `DT_NEEDED`
63+
entry, ELF/Linux) or `-lleanhazmat_bls` (a Mach-O `LC_LOAD_DYLIB`,
64+
Apple), so the loader pulls Bls's shared lib and resolves `blst_*`,
65+
mirroring how `LeanHazmatSha256`'s `.so` gains `libcrypto` on each
66+
platform. That link reference is invisible to Lake's scheduler, so the
67+
`extern_lib` folds Bls's shared-lib build into its own dependency trace
68+
(`Job.zipWith` over `findExternLib? `libleanhazmat_bls`) to keep clean
69+
parallel builds from racing ahead of Bls's `.so`.
6870

6971
## Trust boundary
7072

packages/LeanHazmatKzg/lakefile.lean

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,21 @@ package LeanHazmatKzg where
4444
licenseFiles := #["../../LICENSE"]
4545
-- Make this package's *shared* lib depend on LeanHazmatBls's shared lib
4646
-- so the precompiled module `.so` resolves c-kzg's `blst_*` references
47-
-- at load: `-l:libleanhazmat_bls.so` adds a `DT_NEEDED`, `-L` finds it
48-
-- at link, and the `-rpath` finds it at `dlopen`. This mirrors exactly
49-
-- how LeanHazmatSha256's `.so` gains `NEEDED libcrypto.so.3`. blst lives
50-
-- in Bls's archive (not duplicated here), so the final exe link still
51-
-- sees one blst copy via normal `extern_lib` propagation, no
47+
-- at load: `-L` finds it at link, `-rpath` finds it at `dlopen`, and the
48+
-- `-l` itself is platform-specific (`if Platform.isOSX` below; see
49+
-- ARCHITECTURE.md "Sharing one blst" for the ELF/Mach-O rationale). This
50+
-- mirrors exactly how LeanHazmatSha256's `.so` gains `libcrypto`. blst
51+
-- lives in Bls's archive (not duplicated here), so the final exe link
52+
-- still sees one blst copy via normal `extern_lib` propagation, no
5253
-- duplicate symbols. (Monorepo path; a standalone Kzg mirror resolves
5354
-- Bls through its git `require`.)
5455
moreLinkArgs := Id.run do
5556
let d := unsafe blsLibDir
56-
#["-L" ++ d, "-l:libleanhazmat_bls.so", "-Wl,-rpath," ++ d]
57+
if Platform.isOSX then
58+
#["-L" ++ d, "-lleanhazmat_bls", "-Wl,-rpath," ++ d]
59+
else
60+
let lib := nameToSharedLib "leanhazmat_bls"
61+
#["-L" ++ d, "-l:" ++ lib, "-Wl,-rpath," ++ d]
5762

5863
-- Shares LeanHazmatBls's blst, the single blst owner for the family.
5964
require LeanHazmatBls from "../LeanHazmatBls"
@@ -126,9 +131,9 @@ extern_lib libleanhazmat_kzg pkg := do
126131
let staticJob ← buildStaticLib (pkg.staticLibDir / name) #[shimO, ckzgO, setupO]
127132
-- Order LeanHazmatBls's *shared* lib before this archive (and therefore
128133
-- before the `.so` derived from it). Our shared lib references it via
129-
-- `moreLinkArgs` (`-l:libleanhazmat_bls.so`), but that path is invisible
130-
-- to Lake's scheduler, so a clean parallel build can otherwise link this
131-
-- `.so` before Bls's `.so` exists ("unable to find -l:libleanhazmat_bls.so").
134+
-- `moreLinkArgs`'s platform-specific `-l`, but that path is invisible to
135+
-- Lake's scheduler, so a clean parallel build can otherwise link this
136+
-- `.so` before Bls's `.so` exists ("unable to find" the missing lib).
132137
-- `Job.zipWith` folds Bls's shared-lib build into this job's dependency
133138
-- trace (keeping this archive's path as the value), making the ordering
134139
-- explicit.
@@ -142,8 +147,8 @@ extern_lib libleanhazmat_kzg pkg := do
142147
lean_lib LeanHazmatKzg where
143148
-- Precompiled so `native_decide` (here and downstream) finds the KZG
144149
-- externs as loaded precompiled symbols. The module `.so` resolves
145-
-- `blst_*` at load via the `DT_NEEDED` + `-rpath` on Bls's shared lib
146-
-- that `moreLinkArgs` adds.
150+
-- `blst_*` at load via the platform-specific `-l` + `-rpath` on Bls's
151+
-- shared lib that `moreLinkArgs` adds.
147152
precompileModules := true
148153

149154
-- KAT gate against the consensus-spec KZG vectors + self-contained

0 commit comments

Comments
 (0)