You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LLGo should provide one source-level debugging architecture across:
native Darwin and Linux;
bare-metal and embedded targets;
WASI runtimes;
browser WebAssembly.
The architecture must not assume that one debugger is available everywhere. It should produce standards-based debug artifacts and a versioned LLGo runtime-description contract, then expose them through the debugger used by each platform:
Platform
Debug artifact
Execution control
Go/LLGo presentation
Native Darwin/Linux
Mach-O/ELF + DWARF
LLDB initially
LLDB Go language extension + LLGo runtime adapter
Embedded
host-side ELF + DWARF; derived .bin/.hex/.uf2 for flashing
GDB Remote through OpenOCD, J-Link, QEMU, or another target probe
GDB adapter first; LLDB through gdb-remote where supported
WASI runtime
Wasm + DWARF
runtime guest-debug stub, initially Wasmtime
Wasm-aware LLDB plus LLGo runtime adapter
Browser Wasm
embedded or external DWARF; name section/source map as fallback
browser DevTools/CDP
WebAssembly Language Extension for LLGo
This proposal is an umbrella. The native LLDB language work remains the focused sub-proposal:
The existing native DWARF work has established a useful foundation, but the current target-build policy prevents that foundation from reaching embedded and WebAssembly artifacts:
every -target build currently sets DebugInfo.AlwaysOmit = true;
target link flags unconditionally include -S;
an explicit -ldflags=-w=false is rejected for those targets;
target configuration already has GDB, OpenOCD, and emulator fields, but the GDB command is not propagated into the build result and LLGo has no debug-session command.
Issue #2154 alone cannot address these gaps. An LLDB language extension handles source-language and runtime presentation, but:
embedded debugging normally uses GDB Remote through a probe or emulator;
a browser cannot load an LLDB plugin;
WASI guest debugging depends on runtime and LLDB Wasm support;
runtime stack symbolization through pclntab is different from source-level variable and type inspection through DWARF.
The implementation therefore needs a debugger-independent artifact and runtime contract before adding platform-specific frontends.
Design principles
1. Separate the three debugging layers
Compiler and artifact metadata
DWARF source locations, scopes, variables, and types;
Wasm name section and optional source maps;
pclntab for runtime stack symbolization.
Execution control
native process control;
GDB Remote/OpenOCD/J-Link/QEMU;
Wasmtime guest debug stub;
browser DevTools/CDP.
Go and LLGo presentation
Go expressions and source-language behavior;
string, slice, map, channel, interface, function, and goroutine views;
LLGo runtime-version adapters.
No frontend should own metadata production, target flashing, or runtime layout definitions.
2. Keep DWARF, native symbols, and pclntab independent
Go -w controls DWARF generation/retention.
Go -s controls native symbol-table removal and implies -w unless an explicit -w=false overrides it.
An external pclntab is optionally loaded by the running program. External DWARF is instead located and loaded by a debugger. They may share executable identity and artifact-reporting infrastructure, but they must not share runtime loading behavior or flags.
3. Generate a full debug artifact before deriving deployment artifacts
For embedded targets, the canonical debug artifact is the final linked ELF containing DWARF and target addresses. Flashable .bin, .hex, .uf2, or vendor images are derived from that ELF.
Debug sections are host-side, non-loadable data and should not consume MCU flash or RAM. LLGo should not suppress DWARF generation merely because the deployment format does not carry debug sections.
This follows TinyGo's separation between the linked executable used by GDB and the converted flash binary:
deployable debug builds may place DWARF in a sidecar referenced by the final Wasm module;
production builds may omit DWARF while retaining the name section, pclntab, a source map, or an offline symbol file as explicitly selected.
Post-link transformations such as wasm-opt must either preserve/update debug offsets or run before the final debug artifact is validated.
4. Use a typed debug-artifact policy
The build layer should receive a typed policy rather than parse command strings:
embedded: DWARF remains in the executable or Wasm module;
external: the executable/module identifies a separate debugger-owned DWARF artifact;
host: retain a full host-side debug executable while producing a separate deployment artifact;
none: effective -w; do not generate LLGo DWARF.
Initial defaults:
native executable: embedded;
embedded target: host;
WASI: embedded;
browser development: embedded;
browser deployment: explicit external or none.
The exact user-facing spelling can be introduced in a dedicated flag PR, but it must be parsed through the shared typed flag/config path and must not overload -w or the pclntab option.
5. Keep the producer identity and runtime contract versioned
The initial compatibility path remains:
emit DW_AT_producer = LLGo;
emit DW_LANG_C while the selected consumer lacks Go-language support;
emit a small versioned LLGo debugger marker;
validate Go-oriented types, scopes, and locations independently of debugger behavior.
The marker should identify a debugger ABI/runtime schema, not only a compiler version. Native binaries may retain the existing symbol marker; Wasm should use an equivalent retained custom-section record when a symbol is not reliably discoverable.
Switching to DW_LANG_Go must be a per-consumer capability decision. Native LLDB support alone must not make embedded GDB or browser DevTools lose otherwise usable variables.
6. Share runtime layout definitions, not debugger implementation code
LLGo should define one versioned description of relevant runtime values and operations:
strings and slices;
interfaces;
maps and channels;
function values and closures;
goroutine state and stack ownership;
target pointer size, endianness, and ABI version.
LLDB, GDB, and browser adapters may have different implementation languages and APIs, but must be tested against the same fixtures and schema versions. Unsupported marker/schema versions should fail presentation clearly without preventing ordinary low-level debugging.
Platform plans
Native Darwin and Linux
Use the existing standards-tested DWARF and stock-LLDB compatibility path. Implement #2154 as two layers:
a generic LLDB Go language layer;
LLGo-specific runtime adapters selected by the versioned marker.
Continue using DW_LANG_C until the LLDB Go layer passes the full matrix. Linux GDB support may reuse the common runtime schema, but is not required to block the first native LLDB milestone.
Embedded and bare metal
Replace the global target AlwaysOmit policy with explicit linker/format capabilities.
Retain the final ELF with DWARF and derive the flash image from it.
Activate the existing target GDB/OpenOCD/emulator configuration.
Validate a manual GDB Remote session before adding orchestration.
Add a command such as llgo debug -target=<target> that:
builds the debug ELF;
starts or connects to the configured OpenOCD/J-Link/QEMU server;
starts GDB or LLDB with the correct executable and remote command;
leaves flashing and server logs diagnosable.
The first acceptance targets should include one emulator and one real probe-supported Cortex-M or RISC-V target. ESP/Xtensa can follow after the generic flow is stable.
WASI runtimes
Allow supported wasm-ld targets to retain DWARF.
Verify final DWARF after all link/post-link transformations.
Validate Wasmtime guest debugging using its debug stub and a Wasm-aware LLDB.
Record the minimum supported Wasmtime and LLDB versions; LLGo's current LLVM/LLDB 19 must not be assumed to support the required Wasm debugging protocol.
Reuse the LLGo runtime schema and marker when the debugger can read guest memory and values.
Wasmtime's current guest-debug flow is documented here:
The platform work below should be split into reviewable PRs. Tasks at the same indentation level may proceed independently once their parent is usable.
Task 1: target debug capability and artifact model
Replace unconditional target AlwaysOmit/-S with linker- and format-specific capabilities.
Preserve a host debug artifact and derive embedded deployment formats from it.
Support embedded/external Wasm DWARF without changing pclntab selection.
Report all generated debug/deployment artifacts and sizes.
Test that an embedded host ELF with DWARF produces identical flashed loadable bytes to a stripped control.
Task 2: common LLGo debugger ABI
Specify and test the marker/schema version, target properties, and runtime layout adapters.
Provide shared fixtures for primitives, aliases, recursive types, aggregates, strings, slices, maps, channels, interfaces, functions, closures, and goroutines.
Reject non-LLGo and unsupported-schema targets without breaking raw debugging.
Add GDB formatters/runtime adapters backed by the common schema.
Add LLDB gdb-remote validation where the target architecture is supported.
Add llgo debug orchestration only after the manual artifact/transport contract is stable.
Task 5: WASI debugging
Retain and verify Wasm DWARF for supported WASI targets.
Validate Wasmtime guest debugging and pin a supported tool matrix.
Exercise the common LLGo marker/runtime adapter in guest memory.
Task 6: browser debugging
Validate Chrome DevTools with embedded DWARF.
Add and validate external DWARF packaging, identity, URL resolution, and source remapping.
Implement an LLGo WebAssembly Language Extension.
Test missing sidecars and non-Chrome fallback behavior.
Task 7: cross-platform acceptance and maintenance
Add debugger smoke tests that can run without physical hardware.
Add opt-in hardware/probe tests for representative targets.
Track debug artifact size separately from deployed/loadable size.
Document tool versions, launch recipes, path remapping, and optimized-out expectations.
Acceptance matrix
Every supported frontend must cover the applicable subset of:
source-file and line breakpoints;
stepping and call stacks;
parameters, named results, locals, globals, shadowing, and lexical scopes;
primitive, named, recursive, aggregate, and runtime-backed Go types;
optimized builds with explicit expectations for unavailable/optimized-out values;
panic, trap, division-by-zero, and invalid-memory source locations;
Go/C, Go/host, or Go/JavaScript boundary frames;
PIE/ASLR or Wasm code-offset relocation;
source-path remapping;
missing or stale sidecars;
non-LLGo marker rejection;
final-artifact DWARF verification after LTO and post-link processing.
The CI baseline should include native Darwin/Linux, one emulated embedded target, WASI/Wasmtime, and a headless Chromium fixture. Physical hardware is an additional opt-in lane rather than a requirement for every PR.
Making the runtime parse DWARF for runtime.Caller or panic stacks.
Treating source maps or pclntab as substitutes for DWARF variable/type information.
Requiring one debugger or one transport on every platform.
Globally switching to DW_LANG_Go before each consumer supports it.
Using Delve as the initial frontend; LLGo's LLVM ABI and runtime are not the gc toolchain contract Delve expects.
Solving WebAssembly scheduling, embedded GC, or target flashing beyond what a debug session needs.
Closing this umbrella from a partial platform PR. Subtasks should reference it; it should close only when the agreed platform scope and acceptance matrix are complete.
Summary
LLGo should provide one source-level debugging architecture across:
The architecture must not assume that one debugger is available everywhere. It should produce standards-based debug artifacts and a versioned LLGo runtime-description contract, then expose them through the debugger used by each platform:
.bin/.hex/.uf2for flashinggdb-remotewhere supportedThis proposal is an umbrella. The native LLDB language work remains the focused sub-proposal:
#2154
Motivation
The existing native DWARF work has established a useful foundation, but the current target-build policy prevents that foundation from reaching embedded and WebAssembly artifacts:
-targetbuild currently setsDebugInfo.AlwaysOmit = true;-S;-ldflags=-w=falseis rejected for those targets;Issue #2154 alone cannot address these gaps. An LLDB language extension handles source-language and runtime presentation, but:
The implementation therefore needs a debugger-independent artifact and runtime contract before adding platform-specific frontends.
Design principles
1. Separate the three debugging layers
No frontend should own metadata production, target flashing, or runtime layout definitions.
2. Keep DWARF, native symbols, and pclntab independent
-wcontrols DWARF generation/retention.-scontrols native symbol-table removal and implies-wunless an explicit-w=falseoverrides it.The configurable pclntab implementation is complete for its current native scope:
#2120
An external pclntab is optionally loaded by the running program. External DWARF is instead located and loaded by a debugger. They may share executable identity and artifact-reporting infrastructure, but they must not share runtime loading behavior or flags.
3. Generate a full debug artifact before deriving deployment artifacts
For embedded targets, the canonical debug artifact is the final linked ELF containing DWARF and target addresses. Flashable
.bin,.hex,.uf2, or vendor images are derived from that ELF.Debug sections are host-side, non-loadable data and should not consume MCU flash or RAM. LLGo should not suppress DWARF generation merely because the deployment format does not carry debug sections.
This follows TinyGo's separation between the linked executable used by GDB and the converted flash binary:
https://github.com/tinygo-org/tinygo/blob/888d0c0f978dd5b4bd457644baa597501154fdff/builder/build.go#L39-L50
For Wasm:
Post-link transformations such as
wasm-optmust either preserve/update debug offsets or run before the final debug artifact is validated.4. Use a typed debug-artifact policy
The build layer should receive a typed policy rather than parse command strings:
embedded: DWARF remains in the executable or Wasm module;external: the executable/module identifies a separate debugger-owned DWARF artifact;host: retain a full host-side debug executable while producing a separate deployment artifact;none: effective-w; do not generate LLGo DWARF.Initial defaults:
embedded;host;embedded;embedded;externalornone.The exact user-facing spelling can be introduced in a dedicated flag PR, but it must be parsed through the shared typed flag/config path and must not overload
-wor the pclntab option.5. Keep the producer identity and runtime contract versioned
The initial compatibility path remains:
DW_AT_producer = LLGo;DW_LANG_Cwhile the selected consumer lacks Go-language support;The marker should identify a debugger ABI/runtime schema, not only a compiler version. Native binaries may retain the existing symbol marker; Wasm should use an equivalent retained custom-section record when a symbol is not reliably discoverable.
Switching to
DW_LANG_Gomust be a per-consumer capability decision. Native LLDB support alone must not make embedded GDB or browser DevTools lose otherwise usable variables.6. Share runtime layout definitions, not debugger implementation code
LLGo should define one versioned description of relevant runtime values and operations:
LLDB, GDB, and browser adapters may have different implementation languages and APIs, but must be tested against the same fixtures and schema versions. Unsupported marker/schema versions should fail presentation clearly without preventing ordinary low-level debugging.
Platform plans
Native Darwin and Linux
Use the existing standards-tested DWARF and stock-LLDB compatibility path. Implement #2154 as two layers:
Continue using
DW_LANG_Cuntil the LLDB Go layer passes the full matrix. Linux GDB support may reuse the common runtime schema, but is not required to block the first native LLDB milestone.Embedded and bare metal
AlwaysOmitpolicy with explicit linker/format capabilities.llgo debug -target=<target>that:The first acceptance targets should include one emulator and one real probe-supported Cortex-M or RISC-V target. ESP/Xtensa can follow after the generic flow is stable.
WASI runtimes
Wasmtime's current guest-debug flow is documented here:
https://docs.wasmtime.dev/examples-debugging-guest.html
Browser WebAssembly
The browser path should use DevTools rather than trying to embed LLDB:
Chrome's current Wasm debugger supports source stepping, breakpoints, variables, call stacks, complex values, linear memory, and external DWARF:
https://developer.chrome.com/docs/devtools/wasm
The language-extension API can map raw/source locations, enumerate variables, evaluate expressions, and read Wasm locals, globals, and linear memory:
https://github.com/ChromeDevTools/devtools-frontend/blob/69bfeaa8b2ce9a27d00dd85bad3b65d0aed8aa9c/docs/language_extension_api.md
Source maps are a fallback for locations and symbolication. They are not a replacement for DWARF variables, scopes, or type information.
Existing work and current dependency order
Status checked on 2026-07-23.
-ldflags=-s/-wsemantics and make debug selection typed.build: add initial -ldflags -s and -w support #2113
embedded,external, andnonepackaging.runtime: add configurable pclntab packaging #2120
debug: establish Go DWARF foundation with stock LLDB support #2141
lto: analyze aggregate MethodByName string arguments #2159
[Based on #2159] debug: keep optimization and SSA rewrites DWARF-safe #2143
[Based on #2143] cabi: preserve debug variable homes #2148
[Based on #2143] fix(pclntab): preserve Darwin line sites with DWARF #2157
debug: handle storage-free Python attributes #2145
[Based on #2143 #2145 #2157] build: match cmd/link DWARF defaults #2142
build cache: cold and warm DWARF builds differ in artifact size #2119
The platform work below should be split into reviewable PRs. Tasks at the same indentation level may proceed independently once their parent is usable.
AlwaysOmit/-Swith linker- and format-specific capabilities.proposal: add an LLDB Go language extension for LLGo #2154
DW_LANG_Goonly after the acceptance matrix passes.gdb-remotevalidation where the target architecture is supported.llgo debugorchestration only after the manual artifact/transport contract is stable.Acceptance matrix
Every supported frontend must cover the applicable subset of:
The CI baseline should include native Darwin/Linux, one emulated embedded target, WASI/Wasmtime, and a headless Chromium fixture. Physical hardware is an additional opt-in lane rather than a requirement for every PR.
Related proposals and implementation
Proposal: WebAssembly Support for LLGO #1031
Proposal: Build Target Design #1176
Build Target Task 2: Multi-Platform LLVM Support and Bootable Code Generation #1203
Proposal: Implementing Source Mapping and ABI Optimization Using Custom LLVM Passes #1028
debug: fix print stack for wasm #1181
Non-goals
runtime.Calleror panic stacks.DW_LANG_Gobefore each consumer supports it.