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
LLGo-aware LLDB adapter selected by the runtime marker
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 LLGo-aware LLDB integration 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. The LLGo-aware LLDB adapter handles native 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.
The compile-unit language remains DW_LANG_C for stock-debugger interoperability. Consumer-specific presentation is selected through the LLGo marker/schema and 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:
an installed stock-LLDB launcher and marker-selected adapter;
LLGo-specific runtime adapters selected by the versioned schema.
DW_LANG_C is the supported native contract. 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:
WebAssembly runtime prerequisites for Tasks 5 and 6
The executable wasm runtime is tracked by #2152. The following PRs provide the execution environments that WASI and browser debugging must eventually cover:
These PRs are runtime prerequisites, not debugging completion. Tasks 5 and 6 still require final-artifact DWARF verification, source/variable inspection, execution-control integration, and the common LLGo debugger marker/schema. In particular, #2197's post-link transform must be validated for DWARF preservation, and #2216/#2217 must be covered without assuming that every suspended G is represented by a native debugger thread.
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.
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.
Emitting or supporting DW_LANG_Go, or maintaining a generic debugger Go-language plugin.
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 LLGo-aware LLDB integration 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. The LLGo-aware LLDB adapter handles native 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.
The compile-unit language remains
DW_LANG_Cfor stock-debugger interoperability. Consumer-specific presentation is selected through the LLGo marker/schema and 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:
DW_LANG_Cis the supported native contract. 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-30.
-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
debug: handle storage-free Python attributes #2145
ssa.DebugRef.build: keep return-order fix working with DebugRefs #2183
ssa.DebugRef.[Based on #2215] build: keep select-order repair working with DebugRefs #2184
debug: keep optimization and SSA rewrites DWARF-safe #2143
cabi: preserve debug variable homes #2148
fix(pclntab): preserve Darwin line sites with DWARF #2157
[Based on #2157] build: match cmd/link DWARF defaults #2142
build: stabilize Darwin DWARF debug maps across cache hits #2202
WebAssembly runtime prerequisites for Tasks 5 and 6
The executable wasm runtime is tracked by #2152. The following PRs provide the execution environments that WASI and browser debugging must eventually cover:
runtime/wasm: add single-worker Asyncify scheduler #2192
wasm-optpost-link stage.runtime/wasm: add WASI single-worker scheduler (based on #2192) #2197
runtime/wasm: support blocking primitives (based on #2197) #2198
runtime/wasm: add timers and host event wakeups (based on #2198) #2204
runtime/wasm: add opt-in non-moving collector (based on #2197) #2203
runtime/wasm: preserve suspended goroutine GC roots (based on #2198, #2203) #2207
runtime/wasm: enable cooperative safepoints and GC by default (based on #2204, #2207) #2214
runtime/wasm: add bounded Web worker scheduling (based on #2214) #2216
runtime/wasm: stop all workers for garbage collection (based on #2216) #2217
These PRs are runtime prerequisites, not debugging completion. Tasks 5 and 6 still require final-artifact DWARF verification, source/variable inspection, execution-control integration, and the common LLGo debugger marker/schema. In particular, #2197's post-link transform must be validated for DWARF preservation, and #2216/#2217 must be covered without assuming that every suspended G is represented by a native debugger thread.
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.cmd: add LLGo-aware LLDB launcher with schema validation #2211
cmd: add LLGo-aware LLDB launcher with schema validation #2211
proposal: add LLGo-aware LLDB integration #2154
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: complete LLGo WebAssembly runtime with Asyncify and resumable backends #2152
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_Go, or maintaining a generic debugger Go-language plugin.