# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is `modernc.org/libc` is a partial reimplementation of C libc in pure Go. It is the **runtime** for C programs translated to Go by the `modernc.org/ccgo` transpiler (notably `modernc.org/sqlite`). It is not a standalone library for general use; its API tracks the needs of ccgo-generated code and may change incompatibly between versions. Compatibility note: callers of this package (e.g. `modernc.org/sqlite`) must use the libc version that matches their generated code (see the consumer's `go.mod`). Do not bump the libc version of a downstream consumer in isolation without re-translating its C sources. ## Architectural split: musl-derived vs hand-written This is the most important thing to understand before editing any file. There are two completely separate code paths selected by build constraint: 1. **musl-derived (linux/amd64, arm64, loong64, ppc64le, s390x, riscv64, 386, arm)** — the libc implementation is generated by transpiling musl-libc's C source to Go via `ccgo/v4`. The canonical build tag is: ``` //go:build linux && (amd64 || arm64 || loong64 || ppc64le || s390x || riscv64 || 386 || arm) ``` Generated files for this path: `ccgo_linux_*.go` (~4 MB each, the bulk of the musl translation), the per-arch `musl_*.go` files written by older tooling, and on amd64 the assembly stubs in `abi0_linux_amd64.{go,s}` (generated by `qbecc --abi0wrap`). The hand-written glue lives in `libc_musl.go`, `etc_musl.go`, `mem_brk_musl.go`, `mem_musl.go`, `memgrind_musl.go`, `pthread_musl.go`, `syscall_musl.go`, `aliases.go`, `atomic.go`, `atomic{32,64}.go`, `builtin{32,64}.go`, `libc_linux_statfs.go`. 2. **Hand-written (darwin, freebsd, netbsd, openbsd, illumos, windows, linux/mips64le)** — the libc surface is implemented by hand in Go, with the negated build constraint: ``` //go:build !(linux && (amd64 || arm64 || loong64 || ppc64le || s390x || riscv64 || 386 || arm)) ``` Main files: `libc.go`, `libc_unix.go`, `libc_unix{1,2,3}.go`, `libc_.go`, `libc__.go`, `libc_windows*.go`, `pthread.go`, `mem.go`, `memgrind.go`, `mem_brk.go`. These ports also pull in an older set of ccgo/v3-generated `musl__.go` files for the platform-independent pieces (strtod, math, network, etc.). When you make a change you almost always need to make it in both code paths. Look at the existing build tag of the file you're editing — if it has the long `linux && (amd64 || ...)` form, you're in the musl path, and there's a sibling file with the negated tag that needs the parallel change. ## Common commands Build / sanity check: - `go build ./...` — current platform only - `make editor` — fast iteration: compile tests to /dev/null, build generator, lint generated asm - `make build_all_targets` — full cross-compile sweep across all supported GOOS/GOARCH × build tags (slow; required before submitting per `CONTRIBUTING.md`) - `make vet` — vet, filtering out known abi0 false positives Tests: - `make short-test` — `go test -short` plus log-grep summary - `make test` — full test, 24h timeout - `make xtest` — full test with `-tags=ccgo.dmesg,ccgo.assert` (verbose ccgo debugging) - `make libc-test` — runs only `TestLibc`, which transpiles and runs the musl libc-test suite under `testdata/nsz.repo.hu/libc-test` - `make xlibc` — `TestLibc` with debug tags - `make membrk-test` — full test with `-tags=libc.membrk` (the sbrk-style debug allocator) - Run one test: `go test -v -run TestStrtod -count=1` - Filter `TestLibc` subtests: `go test -v -run TestLibc -re ''` (the `-re` flag is defined in `all_musl_test.go`) Regenerate musl-derived code: - `make generate` — downloads the pinned musl tarball if needed, re-translates it via `go run generator.go` into `$DIR` (default `/tmp/libc`), then rebuilds and short-tests. **Only works on linux.** Output files (`ccgo_linux_*.go`, etc.) are committed to the repo and should not be hand-edited. - `make dev` — same as `generate` but with `GO_GENERATE_DEV=1` and `-tags=ccgo.dmesg,ccgo.assert` for ccgo debugging - Cross-arch generation: set `GO_GENERATE_GOARCH=arm64` etc. The `builder.json` `autogen` regex lists which arches CI auto-regenerates; recent commits like `pi32 auto generate` are these. - Pinned musl version: `internal/archive/archive.go` (currently `musl-7ada6dde6f9dc6a2836c3d92c2f762d35fd229e0`). ## Build tags Functional tags (passed via `-tags=`): - `libc.membrk` — replaces malloc with a fixed-size sbrk-style allocator (1 GiB on 64-bit), `free` becomes a no-op, heap pre-filled with PRNG bytes. Used to surface use-after-free in downstream code. - `libc.memgrind` — wraps the allocator with an audit table; `MemAuditStart` / `MemAuditReport` detect leaks. Adds overhead. - `libc.dmesg` — appends a per-pid log to `/tmp/libc.log` of major libc events. - `libc.strace` — flips `__ccgo_strace` to `true` so every `Xfoo` entry prints a trace line. Heavy. - `libc.memexpvar` — exposes allocator stats through `expvar`. - `ccgo.dmesg`, `ccgo.assert` — consumed by ccgo during generation, not at runtime. `libc.membrk` and `libc.memgrind` are mutually exclusive (the file build tags enforce it). ## Symbol naming conventions ccgo encodes C identifiers with prefixes; the same scheme is used in hand-written files for consistency: - `Xfoo` — externally visible C function `foo` (e.g. `Xmalloc`, `Xputchar`) - `Yfoo` — abi0-wrapped entry to `Xfoo` (linux/amd64 only, in `abi0_linux_amd64.go`) - `Tfoo_t` — typedef'd C type `foo_t` (e.g. `Tsize_t`, `Tssize_t`) - `Sfoo` — struct tag `foo`, `Ufoo` — union tag, `Ffield` — struct field - `X__foo` / `X___foo` — C identifiers starting with `_` get folded; both `__errno_location` and `___errno_location` exist - Lowercase `xfoo`, `_foo` — internal helpers `capi__.go` lists every exported symbol — these are the source-of-truth files ccgo consults when linking against this libc. If you add a new `Xfoo`, add it to the relevant `capi_*.go` files too (the generators do this automatically for the musl path). ## TLS — thread-local state A `*TLS` represents a C "thread". Every `Xfoo` takes one as the first argument. `TLS` is **not safe for concurrent use** — one goroutine per `TLS`. - Inside an `Xmain`-style entry point: `Start(main)` creates the main TLS, locks the goroutine to the OS thread, and runs `main`. - Library callers (e.g. sqlite): call `NewTLS()` per goroutine, `defer tls.Close()`. The first `NewTLS()` is the "main" thread and is *not* OS-thread-locked. - Pthread support: `Xpthread_create` spawns a goroutine with its own TLS. The musl pthread path (`pthread_musl.go`) and the hand-written path (`pthread.go`) implement this separately. - `tls_linux_amd64.{go,s}` — assembly stubs that let abi0-translated code reach `tls.Alloc` / `tls.Free` without going through Go calling conventions. ## Subpackages The repo contains a constellation of subpackages under `errno/`, `fcntl/`, `pthread/`, `signal/`, `stdio/`, `stdlib/`, `sys/{types,socket,stat,vfs,random}`, `time/`, `unistd/`, `pwd/`, `grp/`, `netdb/`, `netinet/`, etc. They are thin: mostly typedefs and integer constants for use by ccgo-generated callers. They are not meant to be imported by general Go code. `internal/archive` — declares the pinned musl version; `internal/autogen` and `internal/overlay` — supporting files for the generator. ## What is generated vs hand-written Code generated (do not hand-edit; regenerate via `make generate`): - `ccgo_linux_*.go` — the bulk of the musl translation, one per Linux arch - `musl__.go` — older ccgo/v3 generation for non-Linux ports and some helpers - `abi0_linux_amd64.{go,s}` — qbecc-generated abi0 wrappers - `capi_*.go` — exported-symbol manifests (regenerated by ccgo) Anything else (`libc.go`, `libc_musl.go`, `libc_.go`, `libc_unix*.go`, `mem*.go`, `pthread*.go`, `aliases.go`, etc.) is hand-written. `generate.go` and `genheaders.go` are old (`//go:build ignore`) ccgo/v3 helpers, kept around but mostly superseded by `generator.go` (ccgo/v4). ## Debugging artifacts in the tree Several large files in the repo root are debug logs / surface dumps (`log`, `log-test`, `log-generate`, `surface.old`, `surface.new`). They are tracked but stale; do not depend on their contents.