Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,46 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.19.0] - 2026-05-29

### Added

- **`DwarfHandling::Remap` — end-to-end DWARF address remapping**
(#143 DWARF Phase 2 increment 3b, `meld-core/src/dwarf.rs`). The
final piece of DWARF Phase 2: reads an input core module's `.debug_*`
sections, translates every code address to the fused code section via
the `AddressRemap` engine (v0.18.0), and re-serializes a single
remapped DWARF set with `gimli::write::Dwarf::from`. New `gimli`
dependency. Exposed via `meld fuse --dwarf remap` (modes: `strip`
(default), `passthrough`, `remap`).
- The per-function instruction offset map is recovered **post-hoc**
by walking the input and final-output operator streams in lockstep
— no state threaded through the merge hot path, and it reflects
whatever rewriting actually happened (including the adapter-wiring
re-rewrite). A per-function operator-count or locals-prefix mismatch
aborts the remap (correct-or-strip).
- **Correct-or-strip throughout.** `gimli::write::Dwarf::from` is
all-or-nothing on addresses, which is used as the safety gate: only
the structurally-invariant code-section base (address 0) is
special-cased; any other unmapped address fails the conversion and
falls back to stripping rather than emitting a wrong address.
- **Single DWARF source** is supported in this increment. Inputs
where more than one core module carries DWARF fall back to `strip`
with a warning (merging independent DWARF unit sets is deferred);
zero DWARF sources is a no-op.
- Encoding uses a three-pass dance so the remapped `.debug_*` land in
the attestation/provenance-hashed bytes (trailing custom sections
don't shift code offsets, so the remap built from pass A is valid
for the final output).
- New loss scenario **LS-D-1** (wrong remapped DWARF address →
de-grounded downstream coverage/breakpoints) is `approved`, gated by
`dwarf::tests::ls_d_1_remap_translates_low_pc` (full gimli
read→convert→write→read oracle). **Residual:** `DW_AT_high_pc`
encoded as a *length* is copied verbatim, so a function's reported
byte length may be off by intra-function LEB drift; `low_pc` and the
line-number program (what debuggers and `pulseengine/witness` use)
are correct.

## [0.18.0] - 2026-05-29

### Added
Expand Down
30 changes: 21 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exclude = [
]

[workspace.package]
version = "0.18.0"
version = "0.19.0"
authors = ["PulseEngine <https://github.com/pulseengine>"]
edition = "2024"
license = "Apache-2.0"
Expand All @@ -23,6 +23,9 @@ wasmparser = { version = "0.246", features = ["component-model"] }
wasm-encoder = { version = "0.246", features = ["component-model"] }
wasmprinter = "0.246"

# DWARF read/write for address remapping (#143 Phase 2)
gimli = "0.31"

# CLI
clap = { version = "4.5", features = ["derive", "cargo"] }

Expand Down
27 changes: 26 additions & 1 deletion meld-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

use anyhow::{Context, Result, anyhow};
use clap::{Parser, Subcommand};
use meld_core::{Fuser, FuserConfig, FusionStats, MemoryStrategy, OutputFormat};
use meld_core::{DwarfHandling, Fuser, FuserConfig, FusionStats, MemoryStrategy, OutputFormat};
use std::fs;
use std::path::Path;
use std::time::Instant;
Expand Down Expand Up @@ -80,6 +80,15 @@ enum Commands {
#[arg(long)]
no_component_provenance: bool,

/// DWARF debug-info handling: `strip` (default — drop all
/// `.debug_*`), `passthrough` (copy verbatim; addresses are
/// wrong against the fused code section), or `remap` (#143 —
/// translate code addresses to the fused code section). `remap`
/// currently supports a single DWARF-bearing input module and
/// falls back to `strip` for multi-source inputs.
#[arg(long, value_name = "MODE", default_value = "strip")]
dwarf: String,

/// Preserve debug names in output
#[arg(long)]
preserve_names: bool,
Expand Down Expand Up @@ -148,6 +157,7 @@ fn main() -> Result<()> {
stats,
no_attestation,
no_component_provenance,
dwarf,
preserve_names,
validate,
component,
Expand All @@ -162,6 +172,7 @@ fn main() -> Result<()> {
stats,
no_attestation,
no_component_provenance,
dwarf,
preserve_names,
validate,
component,
Expand Down Expand Up @@ -218,6 +229,7 @@ fn fuse_command(
show_stats: bool,
no_attestation: bool,
no_component_provenance: bool,
dwarf: String,
preserve_names: bool,
validate: bool,
component: bool,
Expand Down Expand Up @@ -289,6 +301,18 @@ fn fuse_command(
}
}

let dwarf_handling = match dwarf.as_str() {
"strip" => DwarfHandling::Strip,
"passthrough" => DwarfHandling::PassThrough,
"remap" => DwarfHandling::Remap,
other => {
return Err(anyhow!(
"Invalid --dwarf mode: {}. Use 'strip', 'passthrough', or 'remap'",
other
));
}
};

let config = FuserConfig {
memory_strategy,
attestation: !no_attestation,
Expand All @@ -297,6 +321,7 @@ fn fuse_command(
preserve_names,
output_format,
opaque_resources,
dwarf_handling,
..Default::default()
};

Expand Down
3 changes: 3 additions & 0 deletions meld-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] }
wasmparser.workspace = true
wasm-encoder.workspace = true

# DWARF read/write for address remapping (#143 Phase 2 — DwarfHandling::Remap)
gimli.workspace = true

# Error handling
anyhow.workspace = true
thiserror.workspace = true
Expand Down
Loading
Loading