Repository navigation
Description
Background
fbuild symbols <elf> (PR #424) currently relies on the user passing --nm explicitly and auto-derives --cppfilt by string-replacing the toolchain prefix:
pub fn derive_cppfilt_path(nm_path: &Path) -> PathBuf {
// strip "nm" suffix from stem, append "c++filt"
// .../xtensa-esp32s3-elf-nm.exe -> .../xtensa-esp32s3-elf-c++filt.exe
}This works for stock GCC cross-toolchains but is brittle:
- Naming drift: clang ships
llvm-nm+llvm-cxxfilt(noc++filtsymlink on some distros). - macOS Homebrew installs LLVM tools as
gnm/llvm-cxxfiltwith different naming. - PIO ships some chips with
riscv32-unknown-elf-nm(noc++filt; needsllvm-cxxfilt). - Cross-compilers under msys2 sometimes have
.exeonly onnm, notc++filt.
Meanwhile fbuild build emits build_info_<env>.json (see
crates/fbuild-build/src/build_info.rs) with the toolchain paths it
already knows. The natural source of truth.
What's missing today
BuildInfo has cc_path / cxx_path / ar_path / objcopy_path / size_path but no nm_path, cppfilt_path, readelf_path, or objdump_path — exactly the four tools fbuild symbols needs.
Meanwhile FastLED's existing ci/util/symbol_analysis.py reads
board_info[\"aliases\"][\"nm\"] / [\"c++filt\"] / [\"objdump\"] /
[\"readelf\"] from PIO-shape build_info.json (PlatformIO emits an
aliases block; fbuild's emitter does not). So today the Python
consumer works against PIO-built artifacts but cannot trivially work
against fbuild-built artifacts.
Proposed ideal API
1. Extend fbuild_build::build_info::BuildInfo
Add four new string fields, populated by each platform orchestrator
during link metadata collection:
```rust
pub struct BuildInfo {
// existing fields...
pub cc_path: String,
pub cxx_path: String,
pub ar_path: String,
pub objcopy_path: String,
pub size_path: String,
// NEW (this issue)
pub nm_path: String,
pub cppfilt_path: String,
pub readelf_path: String,
pub objdump_path: String,
}
```
All four follow the same "empty string when not applicable"
convention as the existing optional fields. Each platform orchestrator
already knows its toolchain prefix (it derives cc_path from it); the
derivation for the new four is mechanical.
2. Mirror in an aliases block for PIO-shape compatibility
So existing FastLED Python consumers (ci/util/symbol_analysis.py,
ci/inspect_binary.py, etc.) keep working without changes:
```json
{
"": {
"prog_path": "...",
"cc_path": "...",
"nm_path": "...",
"aliases": {
"nm": "",
"c++filt": "",
"readelf": "",
"objdump": ""
}
}
}
```
Pros: drop-in compatibility with PIO consumers. Cons: minor schema
duplication. Worth it for the migration story.
3. Teach fbuild symbols to read it
CLI signature stays back-compat. New behavior:
```
fbuild symbols
[--build-info ] # explicit override
[--nm ] # back-compat manual override (highest precedence)
[--cppfilt ]
```
Resolution order:
--nm/--cppfiltflags (explicit user wins).--build-info <path>if given.- Auto-discovery: walk up from the ELF's directory looking for
build_info_<env>.jsonfirst, thenbuild_info.json. Same lookup
PIO uses, same lookup FastLED Python uses. - PATH-based fallback (today's behavior).
- Hard error with a helpful message that explains the four sources.
This means the common case becomes:
```bash
fbuild symbols .fbuild/build/uno/firmware.elf
or
fbuild symbols .pio/build/esp32s3/firmware.elf
```
— no manual toolchain wiring. Today both forms require --nm because
nothing knows where to find the cross-tool.
4. Use the same aliases block from a future fbuild build --symbol-analysis
fbuild build --symbol-analysis already exists but is the old coarse
report. When the unified analyzer (PR #424 + #427) is wired in
post-merge, the orchestrator should resolve toolchain paths via the
same aliases block it just emitted, so there is exactly one source
of truth.
5. Documentation
docs/symbols.md (new): tutorial that walks through running the
analyzer against a stock bash compile esp32s3 --examples Blink
output and against a fbuild build output, showing how the same JSON
shape works for both.
Scope and ordering
This is a non-trivial schema change (touches every platform
orchestrator: AVR, ESP32, RP2040, STM32, Teensy, ESP8266, NRF52, SAM,
Renesas, Apollo3, LPC, Silabs, CH32V) plus the build_info emitter +
CLI plumbing. Not blocking merge of #424 / #427 — those land with
the auto-derive path that works for ESP-IDF GCC, the dominant case.
This issue is the cleanup follow-up that:
- Adds the four toolchain fields to
BuildInfo. - Adds the
aliasesmirror for PIO compatibility. - Teaches the CLI to auto-locate build_info.json.
- Updates the orchestrators to pass the new paths.
- Updates docs.
Related
- PR feat(symbols): standalone
fbuild symbols <elf>fine-grained bloat report #424 (initialfbuild symbols) - PR feat(symbols): map-derived synthesis for anonymous rodata pools (closes #425) #427 (rodata synthesis, closes fbuild symbols: extract owning symbol from .rodata.<mangled>.str1.N input sections (377 KB of rodata currently buckets at object level) #425)
- FastLED
ci/util/symbol_analysis.py(existing consumer ofaliases.{nm,c++filt,readelf,objdump}) - FastLED #2773 (binary-size meta tracker)
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTriage
Activity
feat(symbols): use running-process for c++filt + project-mode + dual …
feat(symbols): ingest toolchain paths from build_info.json (#436)
feat(bloat): rename `fbuild symbols` to `fbuild bloat` (#443)
feat(bloat): default output dir + path printing on exit; build --bloa…
feat(build): default-on post-link bloat report; --no-bloat-report opt…
test(bloat): end-to-end integration test against FastLED master ESP32…
feat(bloat): graphviz `.dot` back-reference graphs (#463) (#468)