symdis 0.4.0

Symbolic disassembler for Mozilla crash report analysis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# symdis

A CLI tool for disassembling functions from crash reports. Given a module identifier and either a function name or an offset, `symdis` fetches the binary and symbol information from public servers, disassembles the target function, and annotates it with source lines, call targets, and inline frames.

Supports **Windows** (PE), **Linux** (ELF), **macOS** (Mach-O), and **Android** modules. Not limited to Mozilla — works with any module from a crash report, including Microsoft system DLLs, Windows kernel drivers, GPU drivers, Linux system libraries, and macOS frameworks. The tool itself runs on Windows, Linux, and macOS.

Designed primarily for use by AI agents analyzing [Socorro/Crash Stats](https://crash-stats.mozilla.org/) crash reports, but also useful for manual crash triage and reverse engineering.

## Features

- **Fetch symbols and binaries** from Mozilla's [Tecken](https://symbols.mozilla.org/) symbol server, Microsoft/Intel/AMD/NVIDIA symbol servers, [debuginfod](https://sourceware.org/elfutils/Debuginfod.html) servers, the [Snap Store](https://snapcraft.io/) (Ubuntu snaps), APT repositories (Debian/Ubuntu), pacman repositories (Arch Linux), and Mozilla's FTP archive — with automatic CAB decompression, `.tar.xz` extraction, `.pkg` (XAR/cpio) extraction, `.deb` extraction, `.pkg.tar.zst` extraction, and `.apk` (ZIP) extraction
- **Not limited to Mozilla modules**: works with any module from a crash report. On Windows: system DLLs (ntdll, kernel32, kernelbase), kernel drivers (win32kfull.sys, tcpip.sys), GPU drivers — PDB fetched automatically when `.sym` is unavailable. On Linux: system libraries via debuginfod, Snap Store, APT, or pacman. On macOS: framework binaries from FTP archives
- **PDB support** (automatic): PDB is fetched and parsed automatically when `.sym` is unavailable for Windows modules. PDB fetch chain: Tecken → Microsoft → Intel → AMD → NVIDIA. Includes inline frames, srcsrv VCS paths, MSVC-demangled function signatures, and C++ type information for `field-layout`. Use `--pdb` to skip the `.sym` attempt when you know it's unavailable (saves one round-trip)
- **C++ type information**: the `field-layout` command extracts class/struct/union field layouts from PDB type data (TPI stream) — identify which field is at a given byte offset in a crash. Works with Mozilla PDBs (535K+ types) and some Microsoft public PDBs (ntdll.pdb has hundreds of types including `_RTL_CRITICAL_SECTION`, `_PEB`, `_TEB`)
- **Cross-platform module analysis**: PE/Windows (via section table), ELF/Linux (via PT_LOAD segments), Mach-O/macOS (including fat/universal binaries), and Android (Fenix + Focus via APK extraction). symdis itself runs on Windows, Linux, and macOS — it analyzes crash dumps from any platform regardless of where it runs
- **Find functions** by exact name, substring match (`--fuzzy`), or by RVA/offset; searches FUNC records, PUBLIC symbols (with demangling), and binary exports
- **Disassemble** x86, x86-64, ARM32, and AArch64 code via [Capstone](https://www.capstone-engine.org/); ARM32 Thumb-2 mode auto-detected from ELF symbol metadata
- **Annotate** instructions with source file/line, resolved call targets (FUNC/PUBLIC/IAT/PLT/GOT/dylib imports), and inline function boundaries
- **Resolve indirect calls**: IAT imports on x86/x86-64, PLT stubs on ARM/AArch64 ELF, `__stubs` entries on Mach-O, GOT slots on Linux ELF, and AArch64 ADRP+LDR+BLR/BR sequences across all platforms
- **Demangle** C++ (Itanium ABI), Rust, and MSVC (`?Name@...`) symbol names automatically (`--no-demangle` to disable)
- **Highlight** a specific offset (e.g., a crash address) in the output
- **Graceful degradation**: binary+sym gives full annotated disassembly; binary-only gives raw disassembly; sym-only gives function metadata
- **Text and JSON** output formats (`--format text|json`)
- **Configurable** via TOML config file, environment variables, and CLI flags with layered precedence
- **Local cache** with WinDbg-compatible layout, atomic writes, negative-cache markers, and `_NT_SYMBOL_PATH` integration

## Installation

Pre-built binaries (fastest):

```bash
cargo binstall symdis
```

From source:

```bash
cargo install symdis
```

Or clone and build:

```bash
git clone https://github.com/yjugl/symdis.git
cd symdis
cargo install --path .
```

## Quick Start

```bash
# Disassemble the function containing a specific offset, with crash address highlight
symdis disasm \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --code-file xul.dll --code-id 68d1a3cd87be000 \
    --offset 0x0144c8d2 --highlight-offset 0x0144c8d2

# Fuzzy name search
symdis disasm \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --code-file xul.dll --code-id 68d1a3cd87be000 \
    --function ProcessIncomingMessages --fuzzy

# Linux module with FTP archive fallback
symdis disasm \
    --debug-file libxul.so \
    --debug-id 669D6B010E4BF04FF9B3F43CCF735A340 \
    --code-file libxul.so \
    --code-id 016b9d664b0e4ff0f9b3f43ccf735a3482db0fd6 \
    --version 147.0.3 --channel release \
    --offset 0x4616fda --highlight-offset 0x4616fda

# macOS module (fat/universal binary from PKG archive)
symdis disasm \
    --debug-file XUL \
    --debug-id EA25538ED7533E56A4263F6D7050F3D20 \
    --code-file XUL \
    --code-id ea25538ed7533e56a4263f6d7050f3d2 \
    --version 140.6.0esr --channel esr \
    --offset 0x1cb6dd --highlight-offset 0x1cb6dd

# JSON output
symdis disasm \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --code-file xul.dll --code-id 68d1a3cd87be000 \
    --function ProcessIncomingMessages --fuzzy \
    --format json
```

## Example Output

### Text (default)

Output is abbreviated — the tool prints all instructions in the function.

```
; Module: xul.dll (xul.pdb / EE20BD9ABD8D048B4C4C44205044422E1)
; Function: IPC::Channel::ChannelImpl::ProcessIncomingMessages(...) (RVA: 0x144c490, size: 0xa57)
; Source: hg:hg.mozilla.org/releases/mozilla-esr140:ipc/chromium/src/chrome/common/ipc_channel_win.cc:0b8c...
; Architecture: x86
; Data sources: binary+sym
;
    ; .../ipc_channel_win.cc:...:264
    0x0144c490:  push    ebp
    0x0144c491:  mov     ebp, esp
    0x0144c493:  push    ebx
    0x0144c494:  push    edi
    0x0144c495:  push    esi
    0x0144c496:  and     esp, 0xfffffff8
    0x0144c499:  sub     esp, 0xf8
    ; .../ipc_channel_win.cc:...:269
    0x0144c4b8:  test    cl, cl
    ...
    ; [inline] mozilla::UniquePtr<...>::get() const (.../ipc_channel_win.cc:...:305)
    ; .../mfbt/UniquePtr.h:...:399
    0x0144c4d5:  mov     ebx, dword ptr [edi + 0x8c]
    ; [end inline] mozilla::UniquePtr<...>::get() const
    ...
    0x0144c801:  call    0x1463290              ; IPC::Channel::ChannelImpl::AcceptHandles(IPC::Message&)
    ...
==> 0x0144c8cd:  call    dword ptr [0x173b5260]  ; [indirect]
    0x0144c8d3:  add     esp, 4
```

### JSON

```json
{
  "module": {
    "debug_file": "xul.pdb",
    "debug_id": "EE20BD9ABD8D048B4C4C44205044422E1",
    "code_file": "xul.dll",
    "arch": "x86"
  },
  "function": {
    "name": "IPC::Channel::ChannelImpl::ProcessIncomingMessages(...)",
    "address": "0x144c490",
    "size": "0xa57",
    "source_file": "hg:hg.mozilla.org/releases/mozilla-esr140:ipc/chromium/..."
  },
  "instructions": [
    {
      "address": "0x144c490",
      "bytes": "55",
      "mnemonic": "push",
      "operands": "ebp",
      "source_file": "hg:hg.mozilla.org/releases/mozilla-esr140:ipc/chromium/...",
      "source_line": 264,
      "highlighted": false,
      "inline_frames": []
    },
    {
      "address": "0x144c801",
      "bytes": "e88a6a0000",
      "mnemonic": "call",
      "operands": "0x1463290",
      "source_file": "hg:hg.mozilla.org/releases/mozilla-esr140:ipc/chromium/...",
      "source_line": 356,
      "highlighted": false,
      "call_target": "IPC::Channel::ChannelImpl::AcceptHandles(IPC::Message&)",
      "inline_frames": []
    }
  ],
  "source": "binary+sym",
  "warnings": []
}
```

## Commands

| Command | Description |
|---|---|
| `disasm` | Disassemble a function from a module |
| `lookup` | Resolve an offset to a symbol, or a name to an address |
| `info` | Show module metadata and PDB type info availability |
| `fetch` | Pre-fetch symbols and binary for a module |
| `field-layout` | Show C++ class/struct field layout from PDB type info |
| `cache` | Manage the local cache (`path`, `size`, `clear`, `list`) |

Run `symdis <command> --help` for full documentation, crash report field mappings, and more examples.

### disasm

The primary command. See the [Quick Start](#quick-start) section above for common examples. Additional examples:

```bash
# Non-Mozilla module (Windows system DLL):
symdis disasm \
    --debug-file ntdll.pdb \
    --debug-id 08A413EE85E91D0377BA33DC3A2641941 \
    --code-file ntdll.dll --code-id 5b6dddee267000 \
    --function NtCreateFile

# Windows kernel driver (PDB auto-fallback, .pdata function bounds):
symdis disasm \
    --debug-file win32kfull.pdb \
    --debug-id 874E89B5C0960A8CE25E012F602168591 \
    --code-file win32kfull.sys --code-id 73E41EF8412000 \
    --function xxxResolveDesktop

# Ubuntu system library (APT backend, auto-detected source package):
symdis disasm \
    --debug-file libgobject-2.0.so.0 \
    --debug-id D5C5BC91262349F50FA62ACC824CB87C0 \
    --code-id 91bcc5d52326f5490fa62acc824cb87c700d0f8a \
    --apt --distro noble \
    --function g_type_check_instance_cast

# Fenix (Firefox for Android) — MUST use --product fenix:
symdis disasm \
    --debug-file libxul.so \
    --debug-id 9E915B1A91D7345C4FF0753CF13E53280 \
    --code-file libxul.so \
    --code-id 1a5b919ed7915c344ff0753cf13e532814635a84 \
    --product fenix \
    --version 147.0.3 --channel release \
    --offset 0x03fc39d4 --highlight-offset 0x03fc39d4

# Skip .sym lookup, go straight to PDB (saves one round-trip):
symdis disasm \
    --debug-file ntdll.pdb \
    --debug-id 08A413EE85E91D0377BA33DC3A2641941 \
    --code-file ntdll.dll --code-id 5b6dddee267000 \
    --function NtCreateFile --pdb
```

### lookup

Resolves offsets to symbols or symbols to addresses using the `.sym` file only (no binary needed). Searches both FUNC and PUBLIC symbols.

```bash
# Resolve an offset to a symbol name:
symdis lookup \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --offset 0x0144c8d2

# Find a function's address by name (substring match):
symdis lookup \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --function ProcessIncomingMessages --fuzzy
```

### info

Shows module metadata: OS, architecture, function count, sym/binary availability, and PDB type info availability. Use `--pdb` to fetch the PDB and check if `field-layout` will work.

```bash
# Check module metadata and sym/binary availability:
symdis info \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --code-file xul.dll --code-id 68d1a3cd87be000

# Check a non-Mozilla module and probe PDB type info:
symdis info \
    --debug-file ntdll.pdb \
    --debug-id 08A413EE85E91D0377BA33DC3A2641941 \
    --code-file ntdll.dll --code-id 5b6dddee267000 \
    --pdb
```

### fetch

Pre-fetches sym and binary into the local cache so subsequent `disasm` calls are instant.

```bash
# Pre-fetch a Windows module:
symdis fetch \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --code-file xul.dll --code-id 68d1a3cd87be000

# Pre-fetch a Linux module with FTP archive fallback:
symdis fetch \
    --debug-file libxul.so \
    --debug-id 669D6B010E4BF04FF9B3F43CCF735A340 \
    --code-file libxul.so \
    --code-id 016b9d664b0e4ff0f9b3f43ccf735a3482db0fd6 \
    --version 147.0.3 --channel release

# Pre-fetch PDB + binary (skips .sym, also checks type info for field-layout):
symdis fetch \
    --debug-file ntdll.pdb \
    --debug-id 08A413EE85E91D0377BA33DC3A2641941 \
    --code-file ntdll.dll --code-id 5b6dddee267000 \
    --pdb
```

### field-layout

Extracts C++ class/struct/union field layouts from PDB type information (TPI stream). Use `--offset` to identify which field is at a given byte offset in a crash.

```bash
# Show layout of a Mozilla type:
symdis field-layout \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --type nsFrameLoader

# Find which field is at offset 0x4c:
symdis field-layout \
    --debug-file xul.pdb \
    --debug-id EE20BD9ABD8D048B4C4C44205044422E1 \
    --type nsFrameLoader --offset 0x4c

# Windows system type from ntdll.pdb:
symdis field-layout \
    --debug-file ntdll.pdb \
    --debug-id 08A413EE85E91D0377BA33DC3A2641941 \
    --type _RTL_CRITICAL_SECTION
```

### cache

Manage the local cache.

```bash
symdis cache path                             # Print cache directory path
symdis cache size                             # Show total cache size
symdis cache clear                            # Delete all cached files
symdis cache clear --older-than 30            # Delete files older than 30 days
symdis cache list --debug-file xul.pdb        # List cached artifacts for a module
```

## Global Options

| Option | Default | Description |
|---|---|---|
| `--cache-dir <PATH>` | auto-detected | Cache directory path |
| `--format <FMT>` | `text` | Output format: `text` or `json` |
| `--no-demangle` | off | Disable C++/Rust symbol demangling |
| `-v` / `-vv` | off | Verbose output (info / debug) |
| `--offline` | off | Skip network requests; use only cached data |

## Configuration

Settings are resolved with layered precedence: **defaults < config file < environment variables < CLI flags**.

### Config File

Location (checked in order):
1. `SYMDIS_CONFIG` environment variable
2. Platform default: `%APPDATA%\symdis\config.toml` (Windows) or `~/.config/symdis/config.toml` (Linux/macOS)

```toml
[cache]
dir = "D:\\SymbolCache\\symdis"
miss_ttl_hours = 48

[symbols]
servers = [
    "https://symbols.mozilla.org/",
    "https://msdl.microsoft.com/download/symbols",
]
debuginfod_urls = ["https://debuginfod.elfutils.org/"]

[disassembly]
syntax = "intel"        # "intel" or "att"
max_instructions = 2000

[output]
format = "text"         # "text" or "json"

[network]
timeout_seconds = 30
user_agent = "symdis/0.1.0"
offline = false
```

### Environment Variables

| Variable | Description |
|---|---|
| `SYMDIS_CONFIG` | Override config file path |
| `SYMDIS_CACHE_DIR` | Override cache directory |
| `SYMDIS_SYMBOL_SERVERS` | Comma-separated symbol server URLs |
| `DEBUGINFOD_URLS` | Space-separated debuginfod server URLs |
| `_NT_SYMBOL_PATH` | Windows symbol path (used for cache directory resolution) |

## Update Check

On each run, symdis checks [crates.io](https://crates.io/crates/symdis) in the background for a newer version. If one is found, a notice is printed to stderr after the command completes. The check is cached for 24 hours and can be disabled by setting `MOZTOOLS_UPDATE_CHECK=0`.

## Cache

Downloaded symbol files and binaries are cached locally. The cache directory is resolved in this order:

1. `--cache-dir` CLI flag
2. `SYMDIS_CACHE_DIR` environment variable
3. Config file `[cache] dir` setting
4. `_NT_SYMBOL_PATH` (Windows) -- uses the cache path from `SRV*<cache>*<server>` entries
5. Platform default (`%LOCALAPPDATA%\symdis\cache`, `~/.cache/symdis`, or `~/Library/Caches/symdis`)

The cache uses WinDbg-compatible flat layout (`<file>/<id>/<file>`) so it can be shared with other symbol tools.

## Data and Privacy

symdis processes only **publicly available data**:

- **Inputs**: Module identifiers (debug file, debug ID, code file, code ID), function names, and offsets — all from the public portions of crash reports on [Crash Stats](https://crash-stats.mozilla.org/).
- **Downloads**: Symbol files and binaries from public symbol servers (Mozilla Tecken, Microsoft, Intel, AMD, NVIDIA, debuginfod) and public archives (Mozilla FTP, Snap Store, APT, pacman).
- **Does NOT process**: Minidumps, memory contents, crash annotations, user comments, URLs, email addresses, or any other [protected data](https://crash-stats.mozilla.org/documentation/protected_data_access/).

When using symdis — whether manually or through an AI agent — only provide data from **publicly accessible crash report fields** (stack traces, module lists, release information). Do not pass [protected crash report data](https://crash-stats.mozilla.org/documentation/protected_data_access/) (such as user comments, email addresses, or URLs from crash annotations) to symdis or to AI tools analyzing crash reports.

For Mozilla's policies on using AI tools in development, see [AI and Coding](https://firefox-source-docs.mozilla.org/contributing/ai-coding.html). For contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).

## License

This project is licensed under the [Mozilla Public License 2.0](LICENSE).