dlsym 0.0.1

Memory-safe bindings around the platform's dynamic library loading primitives
Documentation
# Roadmap

A single crate, `dlsym`:

| Part                          | Role                                                     |
|-------------------------------|----------------------------------------------------------|
| library (default)             | Runtime loader. Only depends on `libc` / `windows-sys`.  |
| `codegen` feature             | `dlsym::codegen`: C header → Rust generator, for `build.rs`. |
| `dlsym-gen` binary (`codegen`) | Command-line front end to `dlsym::codegen`.              |

Generated code only needs the default features, and nothing in the pipeline
needs LLVM at build time. The generator has no dependencies beyond `dlsym`
itself; keep it that way, or gate any new one to the binary.

## Design principles

1. **No dangling symbols through safe code.** `Symbol<'lib, T>` borrows its
   `Library`, `ArcSymbol<T>` owns a share of it, and `library!` structs own it
   outright. Raw pointers only leave through `unsafe fn into_inner`.
2. **No `Deref<Target = T>`.** libloading's `*sym` copies the function
   pointer out, after which nothing prevents it from outliving the library.
   Functions are invoked through `call` instead.
3. **Sealed symbol types.** Only `unsafe extern ".." fn` pointers and raw
   data pointers can be requested. `get::<u64>` or a safe `extern "C" fn`
   type is rejected at compile time.
4. **Null is not "not found".** A symbol may legitimately resolve to null;
   `dlerror` is what distinguishes the two (`NullSymbol` vs `SymbolNotFound`).
5. **Every `unsafe` block carries a `// SAFETY:` comment** (enforced by
   clippy lints in `Cargo.toml`).

## Phase 0: Scaffolding (done)

- [x] Workspace, edition 2024, MSRV 1.95, lints
- [x] `Library::open` / `get` / `get_arc` / `close`, `Drop` unloads
- [x] `Symbol` and `ArcSymbol` with tuple-based `call`
- [x] Sealed `SymbolType` / `FnPtr` (0–12 args; `C`, `C-unwind`, `system`, `system-unwind`)
- [x] Unix backend (`dlopen`), `os::unix::LibraryExt` (`open_with_flags`, `this`)
- [x] Windows backend written, **never compiled**
- [x] `library!` macro and the `Bindings` trait
- [x] `codegen` feature and `dlsym-gen` binary: IR, emitter (functions only), CLI; parser stubbed
- [x] Tests against libm, plus a `compile_fail` test that a symbol can't outlive its library

## Phase 1: Core loader hardening → `dlsym` 0.1

- [x] CI: Linux x86_64, macOS aarch64, Windows x86_64 and i686 (msvc), each
      on stable and MSRV; clippy on all three OSes; docs; rustfmt
- [ ] CI: more targets — Linux aarch64, macOS x86_64, windows-gnu, FreeBSD
      (via a VM action). musl only makes sense dynamically linked:
      statically linked musl binaries cannot `dlopen`
- [ ] Windows: compile and test; `SetThreadErrorMode` so failed loads don't
      pop up dialogs; `LOAD_LIBRARY_SEARCH_*` flags; `Library::this()` via
      `GetModuleHandleExW`
- [ ] `OpenOptions` builder in place of raw `c_int` flags
      (`RTLD_NODELETE`, `RTLD_NOLOAD`, `RTLD_DEEPBIND`)
- [ ] glibc `dlvsym` for versioned symbols
- [ ] `dladdr` / `dlinfo`: find the path of a loaded library
- [ ] Data symbols: typed `as_ref` / `as_mut` accessors tied to `'lib`;
      document thread-local (`__thread`) symbol caveats
- [ ] Variadic function pointers (`unsafe extern "C" fn(A, ...)`), as a
      `SymbolType` that is only callable through `into_inner`
- [ ] Name helpers: `lib{name}.so.N`, `lib{name}.dylib`, `{name}.dll`;
      `Library::open_first(&[candidates])`
- [ ] Unload safety docs: TLS destructors, `atexit` handlers and threads
      started by a library are unsound to unload; offer `Library::leak()`
- [ ] Audit `dlerror` thread-safety on every supported Unix
- [ ] **Open question:** should `get` be safe? Every use of a sealed symbol
      type already requires `unsafe`, so its safety contract could move
      there.

## Phase 2: `library!` macro

- [ ] `safe fn` items, asserted by the invocation, mirroring edition 2024
      `unsafe extern` blocks
- [ ] Optional symbols (`#[optional] fn foo()`): methods return `Option`,
      and loading doesn't fail when they're missing
- [ ] `#[link_name = ".."]`, per-block ABI (`extern "system"`), versioned names
- [ ] Data items (`static VERSION: *const c_char;`)
- [ ] Optional lazy resolution (resolve on first call through a `OnceLock`)
- [ ] Function names that are Rust keywords (`stringify!(r#type)` gives `"r#type"`)
- [ ] Stay declarative for as long as possible, and only move to a proc
      macro if diagnostics demand it

## Phase 3: `dlsym-gen` C front end

- [ ] Minimal libclang bindings written with `dlsym::library!` and loaded
      at runtime, so `dlsym` is used to build `dlsym-gen` itself.
      Discovery via `LIBCLANG_PATH`, `llvm-config`, and well-known paths
- [ ] Only emit declarations from the requested header(s), not transitive
      system includes; allowlist and blocklist by name or regex
- [ ] IR and emitter for:
  - [ ] typedefs
  - [ ] opaque structs (zero-sized `#[repr(C)]` + `PhantomData<(*mut u8, PhantomPinned)>`)
  - [ ] complete structs and unions, laid out from clang's size, alignment
        and offsets, with compile-time layout assertions
  - [ ] enums as newtype constants, not Rust enums (an out-of-range value
        from C would be UB)
  - [ ] function pointer types, arrays, bitfields
  - [ ] `#define` integer and string constants, evaluated by clang
- [ ] Doc comment forwarding
- [ ] Record the target triple and clang version in the generated header,
      and warn about platform-dependent layouts

## Phase 4: Safety layer and crate generation

The step from *bindings* to *safe bindings*:

- [ ] `dlsym-gen.toml` annotation file per library:
  - functions that are safe to call
  - pointer nullability; `const char *` ↔ `&CStr`
  - `(ptr, len)` pairs ↔ slices
  - ownership, e.g. "`foo_new` returns an object freed by `foo_free`",
    generating an RAII wrapper whose `Drop` calls the free function and
    which borrows the `library!` struct
  - errors, e.g. "negative return means `errno`", generating `Result`
- [ ] `dlsym-gen crate <header> --name libfoo-dl`: emits a complete crate
      (`Cargo.toml`, raw `sys` module, safe wrapper module, README)
- [ ] `build.rs` API (`dlsym::codegen::Builder`)
- [ ] Possibly ship annotation sets for common system libraries

## Phase 5: Release

- [ ] docs.rs metadata covering Unix and Windows targets
- [ ] Examples (`examples/`) for each feature
- [x] release-plz: versioning, changelog and crates.io publishing on
      `master`, with prebuilt `dlsym-gen` binaries attached to each release
- [ ] 0.1.0 once phases 1–3 land (0.0.x pre-releases until then)