Files
Priyanshu KumarandClaude Opus 5.5 d7d966651c vendor: Ghidra resynced to master 918acd744d, the commit the specifications come from
libsla-sys vendors 0verflowme/ghidra r2sleigh-sleigh-context-12: Ghidra
master 918acd744d (2026-10-13) with PF0 and W2 re-ported onto GP-6554's
ParserContext. The parse-depth check this tree carried is gone: GP-6554
bounds the operand path and operand index itself (LowlevelError), and
context.hh is the fork's exactly. metatype.cc joins CORE (marshal.hh
includes it).

API moves: GP-2493 renames CPUI_EXTRACT to CPUI_ZPULL and adds CPUI_SPULL
(74, CPUI_MAX 75); libsla maps them to AnalysisOp::Extract and the new
SignedExtract, refused at the lift boundary as every analysis op is.
sleigh-compiler's slgh_compile.cc is re-ported onto 918acd744d (main and
run_xml dropped, errors collected), and largeTemporaryWarning is gone with
GP-6316, which raised the temporary limit to 256 bytes and made a larger one
an error.

Census: 20 files, 1477 lines, every one a Sleigh temporary renumbered, one to
one within each function: GP-6237 inserts the cross-build bits at bit 8 of a
unique offset where they were shifted in by 4, so the compiler lays the unique
space out anew. GP-5581's truncated unique reads would have moved C in
manual_limits_O2 and rv_O2 and lost switch-hello-world's aarch64 table; the
two previous commits read them as pieces and through the temporary's copy.
Two r2s command tests named temporaries by their offset (`tmp_11f80_3`); they
read the output with the offset dropped, as the census normaliser does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
2026-10-15 19:20:38 +05:30
..

Overview

This crate provides Rust bindings to the Sleigh library libsla found in NSA's Ghidra, which disassembles processor instructions into p-code. This enables binary analysis programs to analyze arbitrary programs by targeting p-code instead of specific instruction set architectures.

Latest Changes

See change log for latest changes.

Configuration

Building a Sleigh instance requires a compiled sleigh specification (.sla) and a processor specification (.pspec). These can be obtained from the sleigh-config crate.

Processor specification files are responsible for filling in context data defined in sla files. For example, addrsize is variable context defined in the x86 sla file. The x86-64 pspec defines this as 2 for 64-bit addressing while the x86 pspec defines this as 1 for 32-bit addressing. Note the sla file is responsible for interpreting the meaning of these values.

Custom Sleigh Specification

Custom sleigh specification files can be compiled from Rust using the sleigh-compiler crate. Alternatively the original compiler can be built from the Ghidra decompiler source using make sleigh_opt.

Example

Disassemble bytes into native assembly instructions.

let sleigh = GhidraSleigh::builder()
    .processor_spec(sleigh_config::processor_x86::PSPEC_X86_64)?
    .build(sleigh_config::processor_x86::SLA_X86_64)?;

// PUSH RBP instruction is the byte 0x55.
let instructions = InstructionBytes::new(vec![0x55]);

// InstructionBytes is a simple byte loader that does not model multiple address spaces.
// However an address space is required, so for simplicity use the default code space.
let address_space = sleigh.default_code_space();

// Start disassembly from the first byte (index 0)
let instruction_address = Address::new(address_space, 0);

// Confirming this is indeed PUSH RBP.
let native_disassembly = sleigh.disassemble_native(&instructions, instruction_address)?;
assert_eq!(native_disassembly.instruction.mnemonic, "PUSH");
assert_eq!(native_disassembly.instruction.body, "RBP");

Disassemble bytes into pcode instructions. Pcode instructions can be used for program modeling.

let sleigh = GhidraSleigh::builder()
    .processor_spec(sleigh_config::processor_x86::PSPEC_X86_64)?
    .build(sleigh_config::processor_x86::SLA_X86_64)?;

// PUSH RBP
let instructions = InstructionBytes::new(vec![0x55]);
let instruction_address = Address::new(sleigh.default_code_space(), 0);
let pcode_disassembly = sleigh.disassemble_pcode(&instructions, instruction_address)?;
let pcode_instructions = pcode_disassembly.instructions;

assert_eq!(pcode_instructions.len(), 3, "There should be 3 pcode instructions");

// Copy RBP into a temporary
let copy_destination = pcode_instructions[0].output.as_ref().unwrap();
assert_eq!(pcode_instructions[0].op_code, OpCode::Copy);
assert_eq!(sleigh.register_name(&pcode_instructions[0].inputs[0]).unwrap(), "RBP");

// Subtract 8 bytes from RSP
assert_eq!(pcode_instructions[1].op_code, OpCode::Int(IntOp::Subtract));
assert_eq!(sleigh.register_name(&pcode_instructions[1].inputs[0]).unwrap(), "RSP");
assert_eq!(pcode_instructions[1].inputs[1].address.offset, 8);

// Store temporary (RBP) into memory address pointed to by RSP
assert_eq!(pcode_instructions[2].op_code, OpCode::Store);
assert_eq!(sleigh.register_name(&pcode_instructions[2].inputs[1]).unwrap(), "RSP");
assert_eq!(&pcode_instructions[2].inputs[2], copy_destination);