How Ghidra's SLEIGH Language Works for Defining New Processor Specifications
Ghidra's SLEIGH language compiles human-readable processor descriptions into binary .sla files, which the SleighLanguage class loads at runtime to power disassembly, p-code generation, and emulation without custom Java implementations.
The NationalSecurityAgency/ghidra repository implements processor support through SLEIGH, a domain-specific language that decouples architectural definitions from the core framework. This system enables reverse engineers to add support for new CPUs by writing declarative text files rather than modifying the Java source code directly.
Understanding the SLEIGH Specification File (.slaspec)
A SLEIGH specification resides in a .slaspec file that declares the fundamental properties of a processor architecture. The skeleton processor shipped with Ghidra demonstrates the essential structure in GhidraBuild/Skeleton/data/languages/skel.slaspec.
Key declarations include:
- Endianness and alignment:
define endian=little;anddefine alignment=1;establish byte order and instruction boundaries. - Address spaces:
define space ram type=ram_space size=2 default;creates distinct memory regions for RAM, registers, and I/O. - Registers:
define register offset=0x00 size=1 [ F A C B E D L H I R ];maps names to offsets within a register space. - Context variables:
define register offset=0xf0 size=4 contextreg;allocates bits for processor state flags that affect decoding. - Includes:
@include "skel.sinc"reuses common macro definitions across multiple specifications.
Real-world implementations like the x86 module compose specifications from multiple included files. The x86.slaspec file demonstrates how complex architectures organize definitions across separate .sinc files for maintainability.
The SLEIGH Build Pipeline: From Source to Binary
Before Ghidra can use a specification, the text-based .slaspec must transform into a binary .sla format through a two-stage build process.
Preprocessing with SleighPreprocessor
The SleighPreprocessor class in Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighPreprocessor.java handles the first stage. This component resolves @include directives, expands macro definitions declared with @define, and processes conditional compilation blocks.
The preprocessor constructs a ModuleDefinitionsAdapter that manages include paths and timestamps for stale-file detection, ensuring that changes to included files trigger recompilation.
Compilation via SleighCompileLauncher
The SleighCompileLauncher class orchestrates the transition from preprocessed text to binary format. Located at Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighCompileLauncher.java, this launcher:
- Locates the
.slaspecsource file and checks if compilation is necessary viaSleighLanguage.isSLAStale. - Invokes the native
slghcompiler (a C++ binary) with appropriate flags. - Writes the resulting
.slafile adjacent to the source specification.
The native compiler performs semantic analysis and encodes the specification into an efficient binary representation for runtime consumption.
Runtime Architecture: Loading and Execution
At runtime, Ghidra's analysis engine loads the compiled .sla file to construct an in-memory model of the processor.
Deserializing the .sla Format
The SleighLanguage constructor in Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java manages the loading process. If the binary is missing or stale, the constructor calls reloadLanguage to trigger recompilation.
The actual decoding occurs through SlaFormat.buildDecoder, implemented in Ghidra/pcode/utils/SlaFormat.java:
PackedDecode decoder = SlaFormat.buildDecoder(slaFile);
decode(decoder); // populates SleighLanguage fields
The decode method populates critical data structures including the address space table (spacetable), register hierarchy (registerBuilder), context settings (ContextCache), and the instruction constructor definitions.
Pattern Matching and Constructor Resolution
After decoding, the language builds a decision tree of Constructor objects to resolve machine code to semantic operations. The Constructor class in Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/Constructor.java encapsulates each instruction pattern and its associated p-code templates.
Pattern matching logic resides in the pattern subpackage, with Pattern.java (Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/pattern/Pattern.java) defining the base matching infrastructure. During disassembly, Ghidra traverses the decision tree (DecisionNode) using ParserWalker to identify the first matching constructor, then emits the corresponding p-code operations (OpTpl objects) for emulation and decompilation.
Integrating with Ghidra's Language Service
SleighLanguage implements the Language interface, allowing seamless integration with Ghidra's analysis framework. The class provides:
- Register enumeration via
getRegisters() - Address space factory via
getAddressFactory() - Instruction prototypes via
getPrototype(), which wraps constructors for specific byte sequences
Discovery and instantiation occur through SleighLanguageProvider (Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguageProvider.java). This provider scans the classpath for .slaspec files and creates SleighLanguage instances on demand.
To load a language programmatically:
LanguageService svc = DefaultLanguageService.getLanguageService();
Language lang = svc.getLanguage(new LanguageID("SLEIGH::Skeleton:LE:64:default"));
Extending Ghidra: Adding a Custom Processor
To implement a new processor architecture using Ghidra SLEIGH language definitions:
- Create the directory structure under
Ghidra/Processors/<MyCPU>/data/languages. - Write the specification starting from
skel.slaspec, defining endianness, spaces, registers, and instruction patterns. - Add include files (
.sinc) for reusable macros and common instruction formats. - Declare the processor in
ProcessorInfo.xmlwithin the processor directory. - Build the project using the
BuildSleighAnt target or allow Ghidra to compile on first load.
When Ghidra loads the new language, it executes the same pipeline: preprocessing, native compilation, binary decoding, and constructor tree generation.
Summary
- SLEIGH specifications use
.slaspecfiles to declaratively define processor endianness, memory spaces, registers, and instruction semantics. - The build pipeline involves
SleighPreprocessorfor text expansion andSleighCompileLauncherto invoke the nativeslghcompiler, producing.slabinaries. - Runtime loading occurs through
SleighLanguage, which usesSlaFormat.buildDecoderto deserialize the binary format into Java objects. - Instruction decoding relies on
Constructorobjects organized into decision trees that map bit patterns to p-code operations. - Extension requires only text file creation and XML registration, enabling new architectures without Java development.
Frequently Asked Questions
What is the difference between a .slaspec and a .sla file in Ghidra?
A .slaspec file is the human-readable source code written in the SLEIGH language that defines a processor's instruction set and architecture. A .sla file is the compiled binary output generated by the slgh compiler, which SleighLanguage loads at runtime for efficient disassembly and emulation. The .sla format is optimized for machine parsing, while the .slaspec format prioritizes human maintainability.
How does Ghidra handle changes to SLEIGH specification files?
Ghidra detects stale specifications through SleighLanguage.isSLAStale, which compares timestamps between the .slaspec source and the compiled .sla binary. When the source is newer, the SleighLanguage constructor automatically invokes reloadLanguage, triggering SleighCompileLauncher to rerun the preprocessor and native compiler before loading the updated binary.
Can I debug SLEIGH constructor matching during disassembly?
Yes, you can inspect the constructor decision tree programmatically by casting the Language object to SleighLanguage and iterating over the constructors. Each Constructor object (defined in Constructor.java) exposes its mnemonic, bit pattern, and p-code template through methods like getMnemonic(), getPattern(), and getPcode(), allowing verification of how specific byte sequences map to semantic operations.
What are context variables in SLEIGH and why are they important?
Context variables are special registers defined with the contextreg type that store processor state bits affecting instruction decoding, such as current execution mode or privilege level. These variables enable a single specification to handle architecture variations (like ARM/Thumb mode switching) by allowing constructors to match different patterns based on runtime context state, which is essential for accurate disassembly of variable-length instruction sets.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →