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
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,21 @@ Grab a standalone binary from [Releases](https://github.com/willibrandon/dotside

## What it does

dotsider opens any .NET DLL or EXE and lets you explore it across 8 tabs. Apphost executables are handled as first-class inputs: when an `.exe` has no .NET metadata, dotsider offers to open the companion managed `.dll`. Self-contained single-file apps work too — dotsider reads the bundle, extracts the entry assembly, and analyzes it directly.
dotsider opens .NET DLLs, EXEs, and supported `.wasm` outputs and lets you explore them across 8 tabs. Apphost executables are handled as first-class inputs: when an `.exe` has no .NET metadata, dotsider offers to open the companion managed `.dll`. Self-contained single-file apps work too — dotsider reads the bundle, extracts the entry assembly, and analyzes it directly.

Native AOT binaries get native treatment instead of an empty metadata view. dotsider validates the embedded ReadyToRun header, walks the runtime sections, surfaces imports, exports, load config, recovered AOT types, recovered methods, and frozen string literals, then renders native disassembly for every .NET architecture it recognizes: x64, Arm64, x86, Arm32/Thumb-2, RISC-V64, LoongArch64, and Wasm32.

When the Native AOT build tree is available, dotsider can attach the pre-ILC managed assemblies plus their mstat/DGML sidecars. That fills the metadata tabs from the original managed inputs and shows each method's IL beside its native code.

ReadyToRun (crossgen2) images keep their full managed metadata, and dotsider joins that metadata to the precompiled method bodies in the same file or composite image. You can see IL beside native code, named call targets from import tables, and component assemblies followed in both directions.

Browser-wasm outputs split into two useful views. Point dotsider at `dotnet.native.wasm` and it parses the runtime module's Wasm sections, type/table/memory/global declarations, imports, exports, element and data segments, function bodies, and `dotnet.native.js.symbols`, then renders Wasm32 disassembly with direct-call targets and typed operands named from the same indexes the runtime uses. Point it at a Webcil app assembly such as `WasmConsole.wasm` and it unwraps the managed metadata and IL instead of treating the container as runtime code.

| Tab | What you see |
|-----|-------------|
| **1 General** | Assembly identity, target framework, architecture, dependency table. Press Enter on a reference to drill into it. |
| **2 PE/Metadata** | COFF headers, CLR header, sections, TypeDefs, MethodDefs, AssemblyRefs, custom attributes, resources, debug directory, native imports, exports, load config, ReadyToRun sections, and recovered AOT types. Press `g` on a TypeDef or MethodDef to jump to its IL. |
| **3 IL / Native** | The label reflects the loaded image: **IL Inspector** for managed IL, **Disassembly** for native-only views, and **IL + Native** when IL is paired with native code. Managed assemblies show a namespace/type/method tree with IL disassembly, PDB source spans, Source Link markers, locals, go-to-definition, and hex jumps. Native AOT binaries list recovered functions and render real native disassembly for x64, Arm64, x86, Arm32/Thumb-2, RISC-V64, LoongArch64, and Wasm32 with named call/branch/data targets, `Foo+0x12`, `loc_…` labels, and `MODULE!Function` imports. ReadyToRun images keep the managed tree, mark precompiled methods, and show IL beside native ranges (hot, funclets, cold). Attached pre-ILC sidecars do the same for Native AOT methods. |
| **2 PE/Metadata** | COFF headers, CLR header, PE/Wasm sections, TypeDefs, MethodDefs, AssemblyRefs, custom attributes, resources, debug directory, native imports, exports, load config, ReadyToRun sections, recovered AOT types, and native/Wasm symbols. Press `g` on a TypeDef or MethodDef to jump to its IL. |
| **3 IL / Native** | The label reflects the loaded image: **IL Inspector** for managed IL, **Disassembly** for native-only views, and **IL + Native** when IL is paired with native code. Managed assemblies show a namespace/type/method tree with IL disassembly, PDB source spans, Source Link markers, locals, go-to-definition, and hex jumps. Native AOT binaries and raw Wasm modules list recovered functions and render real disassembly for x64, Arm64, x86, Arm32/Thumb-2, RISC-V64, LoongArch64, and Wasm32 with named call/branch/data targets, `Foo+0x12`, `loc_…` labels, `MODULE!Function` imports, and Wasm function-index calls. ReadyToRun images keep the managed tree, mark precompiled methods, and show IL beside native ranges (hot, funclets, cold). Attached pre-ILC sidecars do the same for Native AOT methods. |
| **4 Strings** | User strings, metadata strings, raw ASCII and UTF-16 binary scans, and frozen AOT string literals, with configurable minimum length. |
| **5 Hex Dump** | Hex editor with vi-style modal editing (read-only by default), byte category coloring, data interpretation panel, jump-to-offset, and vim navigation. |
| **6 Dep Graph** | Visual dependency graph — your assembly at the root, references as nodes, edge weights by TypeRef count. Press Enter on a node to open that assembly. |
Expand Down Expand Up @@ -81,7 +83,7 @@ The binary lands at `src/Dotsider/bin/Debug/net10.0/dotsider`.
## Usage

```
dotsider <assembly.dll|.exe> # TUI mode — interactive assembly explorer
dotsider <assembly.dll|.exe|.wasm> # TUI mode — interactive assembly/native module explorer
dotsider <package.nupkg> # TUI mode — browse NuGet package contents
dotsider diff <left> <right> # TUI mode — assembly comparison; AOT size diff for mstat inputs

Expand Down Expand Up @@ -113,6 +115,8 @@ dotsider analyze MyAotApp.exe --correlate # correlate with pre-ILC assembl
dotsider analyze MyAotApp.exe --correlate Type.Method # IL and native code side by side (name or 0xVA)
dotsider analyze MyR2RApp.dll --r2r-correlate # ReadyToRun stats (precompiled methods, composite)
dotsider analyze MyR2RApp.dll --r2r-correlate Type.Method # IL beside precompiled native (name or 0xVA)
dotsider analyze dotnet.native.wasm --symbols # list SDK Wasm function symbols
dotsider analyze dotnet.native.wasm --disasm 0x1234 # disassemble a Wasm function body
dotsider analyze MyLib.dll --embedded-source Type.Method # print embedded source
dotsider analyze MyLib.dll --deps # assembly references
dotsider analyze MyLib.dll --strings # extract strings
Expand Down Expand Up @@ -224,8 +228,8 @@ dotsider starts with the low-level APIs that ship with .NET, then layers its own

- **`System.Reflection.Metadata`** provides `MetadataReader` for traversing ECMA-335 metadata tables (types, methods, references, custom attributes, string heaps)
- **`System.Reflection.PortableExecutable`** provides `PEReader` for PE structure (COFF header, sections, CLR header, method bodies)
- Custom Native AOT and ReadyToRun readers parse RTR section tables, recovered NativeFormat metadata, frozen objects, mstat/DGML sidecars, native symbols, and precompiled method maps
- From-scratch native decoders render AOT and R2R code for x64, Arm64, x86, Arm32/Thumb-2, RISC-V64, LoongArch64, and Wasm32
- Custom Native AOT, ReadyToRun, WebAssembly, and Webcil readers parse RTR section tables, recovered NativeFormat metadata, frozen objects, mstat/DGML sidecars, native symbols, precompiled method maps, Wasm standard/custom sections, function bodies, SDK symbol maps, and managed `.wasm` app payloads
- From-scratch native decoders render AOT, R2R, native, and Wasm code for x64, Arm64, x86, Arm32/Thumb-2, RISC-V64, LoongArch64, and Wasm32
- **`System.IO.Compression`** handles NuGet packages (which are just ZIP files containing a `.nuspec` manifest and DLLs)

The dynamic analysis tab uses `Microsoft.Diagnostics.NETCore.Client` to connect to a running .NET process via EventPipe — the same diagnostic infrastructure that powers `dotnet-trace` and `dotnet-counters`. It launches your assembly with a reverse-connect diagnostic port, so events are captured from the very first instruction.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "AssemblyAnalyzer"
description: "Core analyzer that reads a .NET assembly and extracts PE, metadata, IL, and string information. Uses PEReader and MetadataReader from the BCL."
description: "Core analyzer that reads .NET assemblies, Webcil app assemblies, native binaries, and raw Wasm modules. It uses BCL metadata/PE readers where possible and routes runtime-native formats through dotsider's format readers for IL, strings, symbols, disassembly, and size data."
slug: api/dotsider.core.analysis.assemblyanalyzer
sidebar:
order: 0
Expand All @@ -10,8 +10,9 @@ sidebar:

**Assembly:** Dotsider.Core.dll

Core analyzer that reads a .NET assembly and extracts PE, metadata, IL, and string information.
Uses [PEReader](https://learn.microsoft.com/dotnet/api/system.reflection.portableexecutable.pereader) and [MetadataReader](https://learn.microsoft.com/dotnet/api/system.reflection.metadata.metadatareader) from the BCL.
Core analyzer that reads .NET assemblies, Webcil app assemblies, native binaries, and raw Wasm
modules. It uses BCL metadata/PE readers where possible and routes runtime-native formats
through dotsider's format readers for IL, strings, symbols, disassembly, and size data.

```csharp
public sealed class AssemblyAnalyzer : IDisposable
Expand Down Expand Up @@ -726,6 +727,29 @@ Gets the TypeRef metadata table entries.
public IReadOnlyList<TypeRefInfo> TypeRefs { get; }
```

### WasmModuleInfo

Parsed WebAssembly module facts when this file is a raw `.wasm` module, or null for
PE, ELF, and Mach-O inputs. The main .NET browser-wasm native module is
`dotnet.native.wasm`.

**Returns:** [WasmModuleInfo](/api/dotsider.core.analysis.models.wasmmoduleinfo/)

```csharp
public WasmModuleInfo? WasmModuleInfo { get; }
```

### WebcilInfo

Parsed Webcil provenance when this analyzer opened a Webcil managed assembly directly or
unwrapped one from a WebAssembly container. Null for PE, raw Wasm, ELF, and Mach-O inputs.

**Returns:** [WebcilInfo](/api/dotsider.core.analysis.models.webcilinfo/)

```csharp
public WebcilInfo? WebcilInfo { get; }
```

## Methods

### AttachPreIlcCompanions()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "AssemblyLoader"
description: "Shared factory for opening assembly files. Handles apphosts (companion .dll redirect), single-file bundles (entry assembly extraction), Native AOT binaries, and direct .dll/.exe loading. Returns an AssemblyOpenResult that preserves the distinction so callers can decide how to present each case (e.g. showing an apphost dialog)."
description: "Shared factory for opening assembly files. Handles apphosts (companion .dll redirect), single-file bundles (entry assembly extraction), Native AOT binaries, raw Wasm modules, and direct .dll/.exe loading. Returns an AssemblyOpenResult that preserves the distinction so callers can decide how to present each case (e.g. showing an apphost dialog)."
slug: api/dotsider.core.analysis.assemblyloader
sidebar:
order: 0
Expand All @@ -11,7 +11,7 @@ sidebar:
**Assembly:** Dotsider.Core.dll

Shared factory for opening assembly files. Handles apphosts (companion .dll redirect),
single-file bundles (entry assembly extraction), Native AOT binaries, and direct
single-file bundles (entry assembly extraction), Native AOT binaries, raw Wasm modules, and direct
.dll/.exe loading. Returns an [AssemblyOpenResult](/api/dotsider.core.analysis.models.assemblyopenresult/) that preserves the
distinction so callers can decide how to present each case (e.g. showing an apphost dialog).

Expand Down Expand Up @@ -39,7 +39,8 @@ An [AssemblyOpenResult](/api/dotsider.core.analysis.models.assemblyopenresult/)
[Direct](/api/dotsider.core.analysis.models.assemblyopenresult.direct/) for regular assemblies,
[ApphostWithCompanion](/api/dotsider.core.analysis.models.assemblyopenresult.apphostwithcompanion/) for native apphosts with a companion .dll,
[BundleEntry](/api/dotsider.core.analysis.models.assemblyopenresult.bundleentry/) for single-file bundles,
or [NativeAot](/api/dotsider.core.analysis.models.assemblyopenresult.nativeaot/) for Native AOT compiled binaries.
[NativeAot](/api/dotsider.core.analysis.models.assemblyopenresult.nativeaot/) for Native AOT compiled binaries,
or [Direct](/api/dotsider.core.analysis.models.assemblyopenresult.direct/) for raw Wasm modules.

```csharp
public static AssemblyOpenResult Open(string filePath)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "AssemblyOpenResult.Direct"
description: "Direct load — the file is a .dll or .exe with metadata, or a native binary with no metadata and no ReadyToRun header (unknown format)."
description: "Direct load — the file is a .dll or .exe with metadata, a raw WebAssembly module, or a native binary with no metadata and no ReadyToRun header (unknown format)."
slug: api/dotsider.core.analysis.models.assemblyopenresult.direct
sidebar:
order: 2
Expand All @@ -10,8 +10,8 @@ sidebar:

**Assembly:** Dotsider.Core.dll

Direct load — the file is a .dll or .exe with metadata, or a native binary
with no metadata and no ReadyToRun header (unknown format).
Direct load — the file is a .dll or .exe with metadata, a raw WebAssembly module,
or a native binary with no metadata and no ReadyToRun header (unknown format).

```csharp
public sealed record AssemblyOpenResult.Direct : AssemblyOpenResult, IEquatable<AssemblyOpenResult>, IEquatable<AssemblyOpenResult.Direct>
Expand All @@ -30,8 +30,8 @@ public sealed record AssemblyOpenResult.Direct : AssemblyOpenResult, IEquatable<

### Direct(AssemblyAnalyzer)

Direct load — the file is a .dll or .exe with metadata, or a native binary
with no metadata and no ReadyToRun header (unknown format).
Direct load — the file is a .dll or .exe with metadata, a raw WebAssembly module,
or a native binary with no metadata and no ReadyToRun header (unknown format).

**Parameters:**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ A native binary with no CLR metadata and no ReadyToRun header (apphost, unknown
**Returns:** [BinaryKind](/api/dotsider.core.analysis.models.binarykind/)

```csharp
Native = 3
Native = 4
```

### NativeAot
Expand All @@ -46,7 +46,7 @@ whose image embeds a validated ReadyToRun header.
**Returns:** [BinaryKind](/api/dotsider.core.analysis.models.binarykind/)

```csharp
NativeAot = 2
NativeAot = 3
```

### ReadyToRun
Expand All @@ -61,3 +61,14 @@ the native bodies are additionally correlated to their managed methods.
ReadyToRun = 1
```

### Wasm

A WebAssembly module, most commonly the `dotnet.native.wasm` runtime module from a
.NET browser-wasm publish. It has native Wasm code but no ECMA-335 metadata of its own.

**Returns:** [BinaryKind](/api/dotsider.core.analysis.models.binarykind/)

```csharp
Wasm = 2
```

Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,14 @@ A crossgen2 ReadyToRun image's method entry-point tables: named, sized function
ReadyToRun = 7
```

### WebAssembly

A WebAssembly module's function/code sections, optionally named from
`dotnet.native.js.symbols`, the Wasm name section, or exports.

**Returns:** [NativeSymbolSource](/api/dotsider.core.analysis.models.nativesymbolsource/)

```csharp
WebAssembly = 8
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: "WasmDataSegmentInfo"
description: "One WebAssembly data segment."
slug: api/dotsider.core.analysis.models.wasmdatasegmentinfo
sidebar:
order: 2
---

**Namespace:** `Dotsider.Core.Analysis.Models`

**Assembly:** Dotsider.Core.dll

One WebAssembly data segment.

```csharp
public sealed record WasmDataSegmentInfo : IEquatable<WasmDataSegmentInfo>
```

## Inheritance

[Object](https://learn.microsoft.com/dotnet/api/system.object) → **WasmDataSegmentInfo**

## Implements

- [IEquatable\<WasmDataSegmentInfo\>](https://learn.microsoft.com/dotnet/api/system.iequatable-1)

## Constructors

### WasmDataSegmentInfo(int, string, long, int)

One WebAssembly data segment.

**Parameters:**

- `Index` ([Int32](https://learn.microsoft.com/dotnet/api/system.int32)): The data segment index.
- `Mode` ([String](https://learn.microsoft.com/dotnet/api/system.string)): The decoded segment mode: active, passive, or active-explicit-memory.
- `FileOffset` ([Int64](https://learn.microsoft.com/dotnet/api/system.int64)): The file offset where the segment's bytes begin.
- `Size` ([Int32](https://learn.microsoft.com/dotnet/api/system.int32)): The segment byte size.

```csharp
public WasmDataSegmentInfo(int Index, string Mode, long FileOffset, int Size)
```

## Properties

### FileOffset

The file offset where the segment's bytes begin.

**Returns:** [Int64](https://learn.microsoft.com/dotnet/api/system.int64)

```csharp
public long FileOffset { get; init; }
```

### Index

The data segment index.

**Returns:** [Int32](https://learn.microsoft.com/dotnet/api/system.int32)

```csharp
public int Index { get; init; }
```

### Mode

The decoded segment mode: active, passive, or active-explicit-memory.

**Returns:** [String](https://learn.microsoft.com/dotnet/api/system.string)

```csharp
public string Mode { get; init; }
```

### Size

The segment byte size.

**Returns:** [Int32](https://learn.microsoft.com/dotnet/api/system.int32)

```csharp
public int Size { get; init; }
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
title: "WasmElementSegmentInfo"
description: "One WebAssembly element segment declaration."
slug: api/dotsider.core.analysis.models.wasmelementsegmentinfo
sidebar:
order: 2
---

**Namespace:** `Dotsider.Core.Analysis.Models`

**Assembly:** Dotsider.Core.dll

One WebAssembly element segment declaration.

```csharp
public sealed record WasmElementSegmentInfo : IEquatable<WasmElementSegmentInfo>
```

## Inheritance

[Object](https://learn.microsoft.com/dotnet/api/system.object) → **WasmElementSegmentInfo**

## Implements

- [IEquatable\<WasmElementSegmentInfo\>](https://learn.microsoft.com/dotnet/api/system.iequatable-1)

## Constructors

### WasmElementSegmentInfo(int, string, int?, string, int)

One WebAssembly element segment declaration.

**Parameters:**

- `Index` ([Int32](https://learn.microsoft.com/dotnet/api/system.int32)): The zero-based element segment index.
- `Mode` ([String](https://learn.microsoft.com/dotnet/api/system.string)): The decoded element segment mode.
- `TableIndex` ([Nullable\<Int32\>](https://learn.microsoft.com/dotnet/api/system.nullable-1)): The table index when the mode records one.
- `ElementType` ([String](https://learn.microsoft.com/dotnet/api/system.string)): The reference type or element kind.
- `ElementCount` ([Int32](https://learn.microsoft.com/dotnet/api/system.int32)): The number of recorded element expressions or indices.

```csharp
public WasmElementSegmentInfo(int Index, string Mode, int? TableIndex, string ElementType, int ElementCount)
```

## Properties

### ElementCount

The number of recorded element expressions or indices.

**Returns:** [Int32](https://learn.microsoft.com/dotnet/api/system.int32)

```csharp
public int ElementCount { get; init; }
```

### ElementType

The reference type or element kind.

**Returns:** [String](https://learn.microsoft.com/dotnet/api/system.string)

```csharp
public string ElementType { get; init; }
```

### Index

The zero-based element segment index.

**Returns:** [Int32](https://learn.microsoft.com/dotnet/api/system.int32)

```csharp
public int Index { get; init; }
```

### Mode

The decoded element segment mode.

**Returns:** [String](https://learn.microsoft.com/dotnet/api/system.string)

```csharp
public string Mode { get; init; }
```

### TableIndex

The table index when the mode records one.

**Returns:** [Nullable\<Int32\>](https://learn.microsoft.com/dotnet/api/system.nullable-1)

```csharp
public int? TableIndex { get; init; }
```

Loading