Compare commits

..

1 Commits

Author SHA1 Message Date
Eric Kilmer 8243a7b4b4 Fix ccache for Windows in GitHub Actions
- Add TrackFileAccess=false to CMAKE_VS_GLOBALS in ccache-msvc.cmake
  to prevent MSBuild from tracking ccache files as build outputs
- Move Windows ccache directory to %TEMP% to avoid Visual Studio's
  file tracking triggering unnecessary rebuilds
- Update cache action path to use platform-specific locations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 09:26:48 -05:00
39 changed files with 128 additions and 1795 deletions
-214
View File
@@ -1,214 +0,0 @@
---
name: debug-test-failures
version: "1.0.0"
description: >
This skill should be used when debugging test failures, build errors, runtime
crashes, or assertion failures in the Sleigh/Ghidra decompiler project. It
covers building with libc++ hardening assertions (ci-head-assertions preset),
running ctest, obtaining stack traces from SIGABRT or crash signals, narrowing
down which datatest triggers a failure, creating patch files for upstream Ghidra
source in src/patches/, and recognizing common C++ bug patterns such as
strict-weak ordering violations in comparators. Relevant when encountering
"the build is failing", "ctest fails", decompiler crashes, or libc++ assertion
errors.
---
# Debugging Test Failures in the Sleigh Project
This project builds the Ghidra decompiler as a standalone C++ library. Ghidra source
is fetched via CMake FetchContent and patches are applied via `git am`. The key
challenge is that bugs live in upstream Ghidra code and fixes must be packaged as
patch files.
## Quick Reference
```bash
# Build with libc++ assertions (catches strict-weak ordering, container misuse, etc.)
cmake --preset ci-head-assertions
cmake --build build/ci-head-assertions
ctest --test-dir build/ci-head-assertions --output-on-failure
# Clean cached Ghidra source (needed after adding/changing patches)
rm -rf build/ci-head-assertions/_deps/ghidrasource-*
# Where the Ghidra C++ source lives after fetch
build/ci-head-assertions/_deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/cpp/
```
## 0. Prerequisites
### Create `CMakeUserPresets.json`
The `ci-head-assertions` preset used throughout this workflow lives in
`CMakeUserPresets.json`, which is not version-controlled and does **not** exist on
a fresh checkout. You must create it in the repository root before any build
commands will work.
Create `/workspaces/sleigh/CMakeUserPresets.json` with this content:
```json
{
"version": 2,
"cmakeMinimumRequired": { "major": 3, "minor": 18, "patch": 0 },
"configurePresets": [
{
"name": "libcxx",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_CXX_FLAGS": "-stdlib=libc++ -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -fstrict-flex-arrays=3 -fstack-clash-protection -fstack-protector-strong",
"CMAKE_EXE_LINKER_FLAGS": "-stdlib=libc++ -lc++abi",
"CMAKE_SHARED_LINKER_FLAGS": "-stdlib=libc++ -lc++abi"
}
},
{
"name": "ci-head",
"hidden": true,
"inherits": ["ci-ubuntu"],
"cacheVariables": { "sleigh_RELEASE_TYPE": "HEAD" }
},
{
"name": "ci-head-assertions",
"binaryDir": "${sourceDir}/build/${presetName}",
"inherits": ["libcxx", "ci-head"],
"generator": "Ninja",
"cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }
}
]
}
```
This file inherits from `ci-ubuntu` (defined in `CMakePresets.json`), so the
repo's version-controlled presets must also be present (they always are in a
normal checkout).
## 1. Build Presets
### The assertions preset
The `ci-head-assertions` preset (created in Section 0 above) links against libc++
with `_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG`. This enables runtime
checks that catch undefined behavior in STL usage — most importantly, it validates
that comparators passed to `std::sort` satisfy strict-weak ordering.
### Other useful presets
| Preset | Source | Purpose |
|--------|--------|---------|
| `ci-head-assertions` | `CMakeUserPresets.json` | HEAD build with libc++ debug assertions (primary debugging preset) |
| `ci-head` | `CMakeUserPresets.json` | HEAD build without assertions (faster, hidden/base preset) |
| `ci-sanitize` | `CMakePresets.json` | Stable build with ASan + UBSan (repo-controlled) |
Note: `ci-head-assertions` and `ci-head` are user-local presets created in
Section 0. `ci-sanitize` is the only sanitizer preset version-controlled in the
repo. Add your own sanitizer presets to `CMakeUserPresets.json` as needed.
## 2. Running Tests
```bash
# Run all tests
ctest --test-dir build/ci-head-assertions --output-on-failure
# Run a specific test
ctest --test-dir build/ci-head-assertions -R decomp_datatest --output-on-failure
```
There are typically 3 tests:
| Test | What it does |
|------|--------------|
| `sleigh_decomp_unittest` | Unit tests (fast) |
| `sleigh_decomp_datatest` | Decompiler data tests — runs XML test files from the `datatests/` directory |
| `sleigh_namespace_std_test` | Namespace compilation test |
The datatest exercises the most code paths and is the most likely to trigger
assertion failures from comparator bugs.
### How datatest works
The test binary loads XML files from the datatests directory in alphabetical order.
Each XML file contains binary data + expected decompiler output patterns. The test
decompiles each function and checks the output against `<stringmatch>` patterns.
The exact command ctest uses (check `tests/CTestTestfile.cmake` for the authoritative version):
```bash
./tests/sleigh_decomp_test \
-sleighpath <build-dir> \
-path <build-dir>/_deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/datatests \
datatests
```
## 3. Getting a Stack Trace from a Crash
When a test crashes (e.g., SIGABRT from an assertion), you need a stack trace to
identify which comparator or code path is the culprit. The test binaries may not
have full debug symbols, so multiple techniques may be needed.
See `references/stack-trace-techniques.md` for the full details on each method.
Here's the summary:
1. **gdb**: Use `gdb -batch` for full demangled backtraces — the simplest method
2. **lldb**: Use `lldb -b` with `-k` for crash-time backtrace commands
3. **LD_PRELOAD backtrace**: Build a small shared library that catches SIGABRT and
prints a backtrace — works when neither debugger can attach (containers)
4. **addr2line**: Resolve raw hex addresses from the backtrace to function names
and source lines
## 4. Narrowing Down Which Test Triggers a Crash
> **Tip**: Often the stack trace from Section 3 identifies the exact function and
> source line, making this step unnecessary. Use this binary search technique only
> when the stack trace is unclear or you need to isolate which specific test input
> triggers the crash.
When you know the test crashes but can't immediately identify the cause from the
stack trace, use binary search on the test XML files.
The idea: copy subsets of test files to a temporary directory and run the test
binary against that subset. Keep halving until you isolate the triggering file.
```bash
cd build/ci-head-assertions
# Copy a subset of test files to a temporary directory
mkdir -p /tmp/test_subset
SRC=_deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/datatests
for f in $(ls $SRC/*.xml | sort | sed -n '1,20p'); do cp "$f" /tmp/test_subset/; done
# Run just those tests
./tests/sleigh_decomp_test -sleighpath . -path /tmp/test_subset datatests
```
Important: some crashes only reproduce when specific files are processed together
(accumulated state from earlier tests), so start by splitting the full set in half
rather than testing individual files. If an individual file doesn't crash alone, try
combining it with files that ran before it in alphabetical order.
## 5. Creating Patches for Ghidra Source
Patches live in `src/patches/HEAD/` (for HEAD builds) and `src/patches/stable/`
(for stable builds). They are applied via `git am` during the CMake FetchContent
step. See `references/patching-workflow.md` for the full step-by-step.
### Quick workflow
1. **Edit the bug** in `build/<preset>/_deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/cpp/`
2. **Commit**: `cd` into the ghidrasource-src dir, `git add` + `git commit`
3. **Generate patches**: `git format-patch -o /workspaces/sleigh/src/patches/HEAD/ <base_commit>..HEAD`
4. **Register** the new patch in `src/setup-ghidra-source.cmake`
5. **Clean and rebuild**: `rm -rf build/<preset>/_deps/ghidrasource-*` then reconfigure
The base commit for HEAD is the `ghidra_head_git_tag` value in `src/setup-ghidra-source.cmake`.
## 6. Common Bug Patterns
The most common bugs are **strict-weak ordering violations** in comparators used
by `std::sort` or `std::set`. These are caught by libc++ assertions at runtime.
Key patterns include null pointer sentinels that don't check self-comparison,
special index early-returns without checking the other operand, and unsafe casts
in `compareDependency` methods of TypePartial* classes.
See `references/common-bug-patterns.md` for detailed before/after code examples,
real-world instances from the codebase (e.g., `PullRecord::operator<` in
`bitfield.cc`, `FlowBlock::compareFinalOrder` in `block.cc`), and a systematic
audit checklist for finding new comparator bugs.
@@ -1,72 +0,0 @@
# Common Bug Patterns in Ghidra Decompiler Source
## Strict-weak ordering violations
The most common class of bugs caught by libc++ assertions. A comparator for
`std::sort` or `std::set` must satisfy:
- **Irreflexivity**: `comp(a, a)` must return `false`
- **Asymmetry**: if `comp(a, b)` is true then `comp(b, a)` must be false
- **Transitivity**: if `comp(a, b)` and `comp(b, c)` then `comp(a, c)`
The libc++ assertion message looks like:
```
strict_weak_ordering_check.h:50: libc++ Hardening assertion
!__comp(*(__first + __a), *(__first + __b)) failed:
Your comparator is not a valid strict-weak ordering
```
### Pattern 1: Null pointer sentinel without self-check
```cpp
// BUG: when both are null, returns true (violates irreflexivity)
if (ptr == nullptr)
return true;
// FIX: only return true when the other is non-null
if (ptr == nullptr)
return (other.ptr != nullptr);
```
Real example: `PullRecord::operator<` in `bitfield.cc` — when both `readOp`
pointers were null, the comparator returned `true`.
### Pattern 2: Special index without self-check
```cpp
// BUG: when comparing the entry point with itself, returns true
if (bl1->getIndex() == 0) return true;
// FIX: check that the other is different
if (bl1->getIndex() == 0) return (bl2->getIndex() != 0);
```
Real example: `FlowBlock::compareFinalOrder` in `block.cc`.
### Pattern 3: Unsafe cast in compareDependency (TypePartial* classes)
The `TypePartialEnum`, `TypePartialStruct`, and `TypePartialUnion` classes have
`compareDependency` methods that cast `op` to their own type. If `op` is actually
the parent container type (not a partial), the cast accesses invalid memory.
```cpp
// BUG: casts op to TypePartialFoo without checking if op IS the container
TypePartialFoo *tp = (TypePartialFoo *) &op;
// FIX: add a guard before the cast
if (container == &op) return 1; // op is our container
TypePartialFoo *tp = (TypePartialFoo *) &op;
```
Real example: `TypePartialEnum::compareDependency` in `type.cc` (patched in 0005).
## How to audit comparators systematically
1. Search for all `operator<`, `::compare`, and `::compareDependency` functions
2. Search for all `sort(`, `stable_sort(`, and `std::set` usage to find which
comparators are actually used in sorting contexts
3. For each comparator, mentally trace the case where both arguments are identical
— does it return false?
4. Check for early-return branches that don't verify the other operand differs
5. For `compareDependency` methods in TypePartial* classes, check for the
container-guard pattern before unsafe casts
@@ -1,153 +0,0 @@
# Patching Workflow for Ghidra Source
## Overview
Ghidra source is fetched via CMake FetchContent from GitHub. Patches are applied
automatically during the configure step using `git am`. The patch files live in
the Sleigh repo and are version-controlled.
## Directory Structure
```
src/patches/
├── HEAD/ # Patches for the HEAD (development) Ghidra commit
│ ├── 0001-Fix-UBSAN-errors-in-decompiler.patch
│ ├── 0002-Use-stroull-instead-of-stroul-to-parse-address-offse.patch
│ └── ...
└── stable/ # Patches for the stable Ghidra release
├── 0001-Fix-UBSAN-errors-in-decompiler.patch
└── ...
```
## Key Configuration
The base commit and patch list are defined in `src/setup-ghidra-source.cmake`:
- `ghidra_head_git_tag`: The pinned HEAD commit hash
- `ghidra_version` / `ghidra_git_tag`: The stable version tag
- `ghidra_patches`: The ordered list of patch files to apply
## Step-by-Step: Creating a New Patch
### 1. Identify the build preset
Determine which build you're fixing. The Ghidra source checkout location depends
on the preset:
```
build/<preset>/_deps/ghidrasource-src/
```
For example:
```
build/ci-head-assertions/_deps/ghidrasource-src/
```
The C++ source files are in:
```
build/<preset>/_deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/cpp/
```
### 2. Make the fix
Edit the file directly in the Ghidra source checkout. The file already has all
existing patches applied, so you're working on top of the current patch stack.
### 3. Commit in the Ghidra source checkout
```bash
cd build/<preset>/_deps/ghidrasource-src
# In a fresh container, git may require identity configuration before committing:
# git config user.name "Your Name" && git config user.email "your@email.com"
git add Ghidra/Features/Decompiler/src/decompile/cpp/<file>.cc
git commit -m "decompiler: Short description
Longer explanation of the bug and fix. Mention what assertion or test
was failing and why."
```
Commit message conventions:
- Prefix with `decompiler:` for decompiler fixes
- First line should be concise (under 72 chars)
- Body explains the *why*, not just the *what*
### 4. Generate patch files
Use `git format-patch` to regenerate ALL patches from the base commit:
```bash
# Get the base commit from src/setup-ghidra-source.cmake
# For HEAD: it's the ghidra_head_git_tag value
# For stable: it's the Ghidra_X.Y.Z_build tag
git format-patch -o /workspaces/sleigh/src/patches/HEAD/ <base_commit>..HEAD
```
This regenerates patches 0001 through 000N. Existing patches will be regenerated
with updated numbering in the `[PATCH N/M]` subject line, but the content stays
the same. The new patch gets the next number.
### 5. Register in CMake
Edit `src/setup-ghidra-source.cmake` and add the new patch file to the appropriate
`ghidra_patches` list:
```cmake
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0006-your-new-patch-name.patch"
```
### 6. Verify from scratch
Clean the cached source so FetchContent re-fetches and applies all patches fresh:
```bash
rm -rf build/<preset>/_deps/ghidrasource-*
cmake --preset <preset>
cmake --build build/<preset>
ctest --test-dir build/<preset> --output-on-failure
```
Watch the configure output for:
```
Applying: <your commit message>
```
If a patch fails to apply, you'll see `Patch failed at NNNN ...` during configure.
## HEAD vs Stable
- **HEAD patches** (`src/patches/HEAD/`): Applied to the development commit. May
include fixes for newly added files (e.g., `bitfield.cc` is HEAD-only).
- **Stable patches** (`src/patches/stable/`): Applied to the release tag. These
are typically a subset of HEAD patches that also apply to stable.
Check `src/setup-ghidra-source.cmake` to see which files are HEAD-only (look for
`if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")` blocks).
## Troubleshooting
### Patch fails to apply
If `git am` fails during configure, the Ghidra source checkout may be in a dirty
state. Clean it:
```bash
rm -rf build/<preset>/_deps/ghidrasource-*
```
Then reconfigure. If the patch itself is wrong, fix it and try again.
### Patches conflict with new Ghidra commit
When updating the pinned Ghidra HEAD commit, existing patches may not apply cleanly.
See the [wiki](https://github.com/lifting-bits/sleigh/wiki/Patching-and-Updating)
for the full rebasing procedure.
### Source already patched from manual edits
If you edited files directly in the checkout and then try to reconfigure, CMake
may try to apply patches on top of already-patched code. Always clean the
`_deps/ghidrasource-*` directories before reconfiguring after generating new
patches.
@@ -1,149 +0,0 @@
# Stack Trace Techniques
When a test binary crashes (typically SIGABRT from a libc++ hardening assertion),
these techniques can be used to identify the exact function and source line.
## Method 1: gdb (simplest)
gdb in batch mode produces full demangled backtraces and works reliably in most
environments, including containers with default seccomp profiles.
```bash
cd build/ci-head-assertions
gdb -batch \
-ex "run -sleighpath . -path _deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/datatests datatests" \
-ex "bt" \
./tests/sleigh_decomp_test
```
gdb will run the program, and when it crashes (e.g., SIGABRT), it automatically
stops and prints the backtrace. The output includes fully demangled C++ function
names and source locations.
If gdb is unavailable, try lldb (Method 2). If neither debugger can attach
(restricted ptrace), fall back to Method 3.
## Method 2: lldb
Use lldb in batch mode with the full test arguments. The key is to handle SIGABRT
so it stops instead of terminating.
**Important**: Use `-k` (one-line-on-crash) for the backtrace command, not `-o`.
Commands passed via `-o` run sequentially after file load, but when lldb hits a
signal stop, it enters its event loop and won't execute subsequent `-o` commands.
The `-k` flag queues commands to run specifically when the target crashes.
```bash
cd build/ci-head-assertions
lldb -b \
-o "process handle SIGABRT --stop true --pass false" \
-o "run -sleighpath . -path _deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/datatests datatests" \
-k "thread backtrace" \
-k "quit" \
-- ./tests/sleigh_decomp_test
```
If lldb shows full symbol names (function + file + line), you're done.
If lldb fails with "personality set failed: Operation not permitted", this is
common in Docker containers where ptrace is restricted. Use Method 3 instead.
## Method 3: LD_PRELOAD Backtrace Library
Build a small shared library that intercepts SIGABRT and prints a backtrace:
```bash
cat > /tmp/bt_preload.c << 'BTEOF'
#include <signal.h>
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void bt_handler(int sig) {
void *array[50];
int size = backtrace(array, 50);
fprintf(stderr, "=== BACKTRACE (signal %d) ===\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
fprintf(stderr, "=== END BACKTRACE ===\n");
signal(sig, SIG_DFL);
raise(sig);
}
__attribute__((constructor))
void install_handler(void) {
signal(SIGABRT, bt_handler);
}
BTEOF
gcc -shared -fPIC -o /tmp/bt_preload.so /tmp/bt_preload.c -rdynamic
```
Run the test with the preloaded library:
```bash
cd build/ci-head-assertions
LD_PRELOAD=/tmp/bt_preload.so ./tests/sleigh_decomp_test \
-sleighpath . \
-path _deps/ghidrasource-src/Ghidra/Features/Decompiler/src/decompile/datatests \
datatests
```
The output will look like:
```
=== BACKTRACE (signal 6) ===
./tests/sleigh_decomp_test(+0xf1a64)[0x55c312f02a64]
./tests/sleigh_decomp_test(+0xf17ea)[0x55c312f027ea]
./tests/sleigh_decomp_test(+0xef732)[0x55c312f00732]
...
=== END BACKTRACE ===
```
The addresses in parentheses (like `+0xf1a64`) are the key — these are offsets
from the binary's load address.
## Method 4: addr2line
Resolve the hex offsets from the backtrace to function names and source lines:
```bash
for addr in 0xf1a64 0xf17ea 0xef732 0x31fbd9 0x31d536; do
echo -n "$addr: "
addr2line -e ./tests/sleigh_decomp_test -f -C "$addr"
echo "---"
done
```
Flags:
- `-e` specifies the executable
- `-f` prints function names
- `-C` demangles C++ names
Example output:
```
0xf1a64: void std::__1::__check_strict_weak_ordering_sorted<...>(...)
/usr/lib/llvm-21/bin/../include/c++/v1/__debug_utils/strict_weak_ordering_check.h:51
---
0x31fbd9: ghidra::BlockGraph::orderBlocks()
/path/to/block.hh:431
---
```
This tells you exactly which sort call and which comparator function triggered
the assertion.
## Putting It All Together
Typical debugging flow:
1. Run the test: `ctest --test-dir build/ci-head-assertions -R decomp_datatest --output-on-failure`
2. See assertion failure → need stack trace
3. Try gdb first (Method 1) — it typically gives the best output with full demangled symbols
4. If gdb is unavailable, try lldb (Method 2)
5. If neither debugger can attach → build LD_PRELOAD library (Method 3)
6. Resolve addresses with addr2line (Method 4) if the backtrace only has hex offsets
7. Now you know which comparator function and sort call is the problem
8. Read the comparator code and check for strict-weak ordering violations
@@ -1,67 +0,0 @@
---
name: sleigh-ghidra-release
description: Update the lifting-bits/sleigh repository for a new upstream Ghidra stable release. Use when bumping Ghidra stable tags or versions, porting or regenerating src/patches/stable from HEAD patches, updating Ghidra source or spec file lists, moving HEAD-only source/header entries into stable, validating local Ghidra builds, or preparing release-update commits and PRs.
---
# Sleigh Ghidra Release
## Overview
Update this repository after NSA publishes a new Ghidra release. Prefer the repo's existing release-update shape: verify the upstream tag, reuse `src/ghidra-stable`, regenerate stable patches mechanically from HEAD patches where possible, update CMake lists, and validate with a local build.
Read [references/release-checklist.md](references/release-checklist.md) when doing the update; it contains the detailed checklist and decision points.
## Workflow
1. Inspect state before changing anything:
- Run `git status --short`.
- Treat existing edits and untracked files as user-owned unless the task clearly says otherwise.
- Keep unrelated files out of release-update commits.
2. Verify the target release:
- Use official upstream sources for release and tag facts.
- Prefer Exa MCP tools for web lookup when available.
- Confirm the tag format, usually `Ghidra_<version>_build`.
3. Prepare `src/ghidra-stable`:
- Use the existing checkout when present.
- If a clone is needed, clone under `src/`.
- Normalize `origin` to HTTPS if the remote uses a local SSH alias:
```bash
git -C src/ghidra-stable remote set-url origin https://github.com/NationalSecurityAgency/ghidra.git
```
- Fetch and check out the release tag:
```bash
git -C src/ghidra-stable fetch origin tag Ghidra_<version>_build --depth=1
git -C src/ghidra-stable checkout --detach Ghidra_<version>_build
git -C src/ghidra-stable config user.name "github-actions[bot]"
git -C src/ghidra-stable config user.email "41898282+github-actions[bot]@users.noreply.github.com"
```
4. Regenerate stable patches mechanically:
- Start from a clean `Ghidra_<version>_build` checkout.
- Try applying HEAD patches in order.
- If a HEAD patch fails, inspect whether it is already upstreamed, still needed with adjusted context, or only applies to HEAD-only code. Abort and retry with the applicable subset when needed.
- Use `format-patch` from `Ghidra_<version>_build..HEAD` to create the new stable patch files.
- Update `src/setup-ghidra-source.cmake` patch ordering to match the regenerated files.
5. Update release metadata and lists:
- Set `ghidra_version` in `src/setup-ghidra-source.cmake`.
- Search for `sleigh_RELEASE_IS_HEAD`; move entries into stable when the release now contains those sources or headers.
- Update `src/spec_files_stable.cmake` from the release checkout's `.slaspec` inventory.
- Check `cmake/packaging.cmake` and reset `PACKAGE_VERSION` to `1` when the release cycle requires it.
6. Validate the exact files CMake will use:
- Re-checkout the release tag in `src/ghidra-stable`.
- Apply `src/patches/stable/*.patch`.
- Configure with `FETCHCONTENT_SOURCE_DIR_GHIDRASOURCE` pointing at the local checkout.
- Build with `cmake --build ... --parallel`.
- Run `ctest`; if no developer-mode configure was used, `No tests were found` may be expected.
7. Review and commit only when asked:
- Use `git diff --cached --stat`, `git diff --cached --name-status`, and `git diff --cached --check`.
- `git diff --check` may flag generated `git format-patch` signature lines; inspect before changing generated patch files.
- Commit messages must follow the repo's 50/72 rule.
@@ -1,4 +0,0 @@
interface:
display_name: "Sleigh Ghidra Release"
short_description: "Update Sleigh for new Ghidra releases"
default_prompt: "Use $sleigh-ghidra-release to update this repo for a new upstream Ghidra release."
@@ -1,78 +0,0 @@
# Release Update Checklist
This checklist is based on the repository wiki page:
https://github.com/lifting-bits/sleigh/wiki/New-Ghidra-Release-Update-Checklist
## Files To Check
- `src/setup-ghidra-source.cmake`: stable version, stable patch list, HEAD pin, source lists.
- `src/patches/stable/`: regenerated patches for the stable Ghidra tag.
- `src/patches/HEAD/`: source patches to port to the new stable release.
- `src/spec_files_stable.cmake`: stable `.slaspec` inventory.
- `src/spec_files_HEAD.cmake`: comparison point for new spec files.
- `CMakeLists.txt`: public Ghidra headers copied into the build include directory.
- `cmake/packaging.cmake`: package version reset when relevant.
## Patch Regeneration
Preferred pattern:
```bash
git -C src/ghidra-stable checkout --detach Ghidra_<version>_build
git -C src/ghidra-stable am --ignore-space-change --ignore-whitespace --no-gpg-sign \
/absolute/path/to/src/patches/HEAD/*.patch
git -C src/ghidra-stable format-patch -o /private/tmp/sleigh-ghidra-stable-patches \
Ghidra_<version>_build..HEAD
```
If a patch fails:
- Run `git -C src/ghidra-stable am --abort`.
- Inspect the failed patch and the target source.
- Decide whether the patch is already upstreamed, still needed with refreshed context, or only applicable to HEAD-only code.
- Reapply the exact applicable patch sequence or subset.
- Regenerate stable patches from the resulting commits; avoid hand-editing patch bodies unless conflict resolution requires it.
## Source And Header Lists
Find HEAD-only gates:
```bash
rg -n "sleigh_RELEASE_IS_HEAD|sleigh_RELEASE_TYPE.*HEAD" \
CMakeLists.txt src cmake support tests
```
When a previously HEAD-only file exists in the new stable release, move it to the normal stable list and leave no dead conditional branch unless the branch still has a purpose.
## Spec Files
Build the stable inventory from the release checkout:
```bash
fd -e slaspec . src/ghidra-stable/Ghidra/Processors
```
Update `src/spec_files_stable.cmake` with sorted
`${ghidrasource_SOURCE_DIR}/...` entries. Compare against both the old stable
list and `src/spec_files_HEAD.cmake`.
## Validation
Verify the generated stable patch files directly:
```bash
git -C src/ghidra-stable checkout --detach Ghidra_<version>_build
git -C src/ghidra-stable am --ignore-space-change --ignore-whitespace --no-gpg-sign \
/absolute/path/to/src/patches/stable/*.patch
```
Then configure and build Sleigh against the local checkout:
```bash
cmake -B build/ghidra-<version>-local -S . \
-DFETCHCONTENT_SOURCE_DIR_GHIDRASOURCE=/absolute/path/to/src/ghidra-stable
cmake --build build/ghidra-<version>-local --parallel
ctest --test-dir build/ghidra-<version>-local --output-on-failure
```
If tests matter for the update, use a developer-mode preset or option. A plain configure may not register tests.
-1
View File
@@ -1 +0,0 @@
../.agents/skills
-37
View File
@@ -1,37 +0,0 @@
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
ENV DEBIAN_FRONTEND=noninteractive
ENV LLVM_VERSION=21
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
gnupg \
wget \
software-properties-common \
&& wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] http://apt.llvm.org/noble/ llvm-toolchain-noble-${LLVM_VERSION} main" \
> /etc/apt/sources.list.d/llvm.list \
&& apt-get update && apt-get install -y \
clang-${LLVM_VERSION} \
clangd-${LLVM_VERSION} \
lld-${LLVM_VERSION} \
llvm-${LLVM_VERSION}-dev \
libclang-${LLVM_VERSION}-dev \
libc++-${LLVM_VERSION}-dev \
libc++abi-${LLVM_VERSION}-dev \
lldb-${LLVM_VERSION} \
cmake \
ninja-build \
git \
zlib1g-dev \
ccache \
doxygen \
graphviz \
&& update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${LLVM_VERSION} 100 \
&& update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${LLVM_VERSION} 100 \
&& update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_VERSION} 100 \
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_VERSION} 100 \
&& update-alternatives --install /usr/bin/lld lld /usr/bin/lld-${LLVM_VERSION} 100 \
&& update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-${LLVM_VERSION} 100 \
&& update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-${LLVM_VERSION} 100 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
-19
View File
@@ -1,19 +0,0 @@
{
"name": "sleigh",
"build": {
"dockerfile": "Dockerfile"
},
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt=seccomp=unconfined",
"--privileged"
],
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cmake-tools",
"llvm-vs-code-extensions.vscode-clangd",
]
}
}
}
+3 -3
View File
@@ -26,7 +26,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Setup Git User for Applying Patches
# See this thread for more details https://github.community/t/github-actions-bot-email-address/17204/5
@@ -152,10 +152,10 @@ jobs:
EOF
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
uses: actions/upload-pages-artifact@v3
with:
path: docs-site
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
uses: actions/deploy-pages@v4
+16 -213
View File
@@ -13,9 +13,6 @@ on:
branches:
- "*"
env:
LLVM_VERSION: 21
jobs:
build:
runs-on: ${{ matrix.os }}
@@ -26,14 +23,9 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
build_type: [RelWithDebInfo, Debug]
release: [stable, HEAD]
exclude:
- os: ubuntu-latest
build_type: Debug
- os: macos-latest
build_type: Debug
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Setup Git User for Applying Patches
# See this thread for more details https://github.community/t/github-actions-bot-email-address/17204/5
@@ -58,7 +50,7 @@ jobs:
- name: Install Windows system dependencies
if: runner.os == 'Windows'
uses: nick-fields/retry@v4
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
@@ -77,7 +69,7 @@ jobs:
- name: Update the cache (ccache)
uses: actions/cache@v5
with:
path: "${{ github.workspace }}/ccache"
path: ${{ runner.os == 'Windows' && env.TEMP || github.workspace }}/ccache
key: ${{ env.CACHE_KEY }}_ccache_${{ env.CACHE_TIMESTAMP }}
restore-keys: |
${{ env.CACHE_KEY }}_ccache_
@@ -88,14 +80,16 @@ jobs:
run: |
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ccache")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_BASEDIR=${CMAKE_CURRENT_SOURCE_DIR}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/ccache\n")
if(WIN32)
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=$ENV{TEMP}/ccache\n")
else()
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/ccache\n")
endif()
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESS=true\n")
# Trial and error to get all files in here
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESSLEVEL=10\n")
# This should be multiplied by the number of compilation jobs and be no
# larger than 5G, which is the cache max size
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_MAXSIZE=400M\n")
# Tell CMake to use ccache
# These only work for Ninja/Makefiles, not VS generators
# But harmless to set for all platforms
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_CXX_COMPILER_LAUNCHER=ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_C_COMPILER_LAUNCHER=ccache\n")
# Clear stats before every build
@@ -222,234 +216,43 @@ jobs:
# DEB Package
- name: Upload the DEB package artifact (RelWithDebInfo only)
if: matrix.build_type == 'RelWithDebInfo' && runner.os == 'Linux'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ env.DEB_PACKAGE_NAME }}
path: ${{ env.DEB_PACKAGE_PATH }}
archive: false
- name: Release DEB package artifact (RelWithDebInfo only)
uses: softprops/action-gh-release@v3.0.0
uses: softprops/action-gh-release@v2.5.0
if: matrix.build_type == 'RelWithDebInfo' && runner.os == 'Linux' && startsWith(github.ref, 'refs/tags/') && matrix.release == 'stable'
with:
files: ${{ env.DEB_PACKAGE_PATH }}
draft: true
# RPM Package
- name: Upload the RPM package artifact (RelWithDebInfo only)
if: matrix.build_type == 'RelWithDebInfo' && runner.os == 'Linux'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ env.RPM_PACKAGE_NAME }}
path: ${{ env.RPM_PACKAGE_PATH }}
archive: false
- name: Release RPM package artifact (RelWithDebInfo only)
uses: softprops/action-gh-release@v3.0.0
uses: softprops/action-gh-release@v2.5.0
if: matrix.build_type == 'RelWithDebInfo' && runner.os == 'Linux' && startsWith(github.ref, 'refs/tags/') && matrix.release == 'stable'
with:
files: ${{ env.RPM_PACKAGE_PATH }}
draft: true
# TGZ Package
- name: Upload the TGZ package artifact (RelWithDebInfo only)
if: matrix.build_type == 'RelWithDebInfo'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ env.TGZ_PACKAGE_NAME }}
path: ${{ env.TGZ_PACKAGE_PATH }}
archive: false
- name: Release TGZ package artifact (RelWithDebInfo only)
uses: softprops/action-gh-release@v3.0.0
uses: softprops/action-gh-release@v2.5.0
if: matrix.build_type == 'RelWithDebInfo' && startsWith(github.ref, 'refs/tags/') && matrix.release == 'stable'
with:
files: ${{ env.TGZ_PACKAGE_PATH }}
draft: true
- name: ccache stats
run: ccache -s
publish-release:
needs: [build]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- run: gh release edit "${{ github.ref_name }}" --draft=false --repo "${{ github.repository }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
sanitizer:
runs-on: ubuntu-latest
permissions:
contents: read
env:
ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=1
UBSAN_OPTIONS: print_stacktrace=1
strategy:
fail-fast: false
matrix:
release: [stable, HEAD]
steps:
- uses: actions/checkout@v7
- name: Setup Git User for Applying Patches
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Install LLVM
run: |
wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh
chmod +x /tmp/llvm.sh
sudo /tmp/llvm.sh $LLVM_VERSION
echo "CXX=clang++-$LLVM_VERSION" >> "$GITHUB_ENV"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache
- name: Generate cache key
shell: cmake -P {0}
run: |
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_KEY=sanitizer_${{ matrix.release }}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_TIMESTAMP=${current_date}\n")
- name: Update the cache (ccache)
uses: actions/cache@v5
with:
path: "${{ github.workspace }}/ccache"
key: ${{ env.CACHE_KEY }}_ccache_${{ env.CACHE_TIMESTAMP }}
restore-keys: |
${{ env.CACHE_KEY }}_ccache_
- name: Setup ccache
working-directory: "${{ github.workspace }}"
shell: cmake -P {0}
run: |
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ccache")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_BASEDIR=${CMAKE_CURRENT_SOURCE_DIR}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESS=true\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESSLEVEL=10\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_MAXSIZE=400M\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_CXX_COMPILER_LAUNCHER=ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_C_COMPILER_LAUNCHER=ccache\n")
execute_process(COMMAND ccache -z)
- name: Configure the project
run: cmake --preset=ci-sanitize
-Dsleigh_RELEASE_TYPE=${{ matrix.release }}
-Dsleigh_BUILD_DOCUMENTATION=OFF
- name: Build the project
run: cmake
--build build/sanitize
-j 4
-v
- name: Test the project
working-directory: build/sanitize
run: ctest -VV
- name: Run the example
run: cmake
--build build/sanitize
-j 4
--target sleigh_example_runner
- name: Run the install target
run: cmake --install build/sanitize
--prefix install
- name: Smoketest sleigh lift
run: |
./install/bin/sleigh-lift --version
./install/bin/sleigh-lift disassemble x86-64.sla 4881ecc00f0000
./install/bin/sleigh-lift pcode x86-64.sla 4881ecc00f0000
- name: ccache stats
run: ccache -s
assertions:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
release: [stable, HEAD]
steps:
- uses: actions/checkout@v7
- name: Setup Git User for Applying Patches
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Install LLVM
run: |
wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh
chmod +x /tmp/llvm.sh
sudo /tmp/llvm.sh $LLVM_VERSION
echo "CXX=clang++-$LLVM_VERSION" >> "$GITHUB_ENV"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache libc++-$LLVM_VERSION-dev libc++abi-$LLVM_VERSION-dev
- name: Generate cache key
shell: cmake -P {0}
run: |
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_KEY=assertions_${{ matrix.release }}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_TIMESTAMP=${current_date}\n")
- name: Update the cache (ccache)
uses: actions/cache@v5
with:
path: "${{ github.workspace }}/ccache"
key: ${{ env.CACHE_KEY }}_ccache_${{ env.CACHE_TIMESTAMP }}
restore-keys: |
${{ env.CACHE_KEY }}_ccache_
- name: Setup ccache
working-directory: "${{ github.workspace }}"
shell: cmake -P {0}
run: |
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ccache")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_BASEDIR=${CMAKE_CURRENT_SOURCE_DIR}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESS=true\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESSLEVEL=10\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_MAXSIZE=400M\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_CXX_COMPILER_LAUNCHER=ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_C_COMPILER_LAUNCHER=ccache\n")
execute_process(COMMAND ccache -z)
- name: Configure the project
run: cmake --preset=ci-assertions
-Dsleigh_RELEASE_TYPE=${{ matrix.release }}
-Dsleigh_BUILD_DOCUMENTATION=OFF
- name: Build the project
run: cmake
--build build/assertions
-j 4
-v
- name: Test the project
working-directory: build/assertions
run: ctest -VV
- name: ccache stats
run: ccache -s
+23 -76
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
@@ -26,90 +26,37 @@ jobs:
python3 scripts/update_ghidra_head.py --ci
# Need this to run further Actions on the newly created PR
# See here for more details https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens
- uses: actions/create-github-app-token@v3
# See here for more details https://github.com/peter-evans/create-pull-request/blob/28fa4848947e0faa7fa50647691d01477589d5e9/docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens
- uses: tibdex/github-app-token@v2
if: steps.head_update.outputs.did_update
id: generate-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Commit, push, and create PR
- name: Create PR
if: steps.head_update.outputs.did_update
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
BRANCH="cron/update-ghidra-${{ steps.head_update.outputs.short_sha }}"
uses: peter-evans/create-pull-request@v8
with:
title: Update Ghidra HEAD to commit ${{ steps.head_update.outputs.short_sha }}
commit-message: |
Bump Ghidra HEAD commit ${{ steps.head_update.outputs.short_sha }}
# Check if PR already exists for this branch
if gh pr list --head "$BRANCH" --json number --jq '.[0].number' | grep -q .; then
echo "PR already exists for branch $BRANCH, skipping"
exit 0
fi
Changed files:
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
${{ steps.head_update.outputs.changed_files }}
# Create branch and commit
git checkout -b "$BRANCH"
git add -A
git commit -m "$(cat <<'EOF'
Bump Ghidra HEAD commit ${{ steps.head_update.outputs.short_sha }}
Commit details:
Changed files:
${{ steps.head_update.outputs.commit_details }}
body: |
Changed files:
${{ steps.head_update.outputs.changed_files }}
${{ steps.head_update.outputs.changed_files }}
Commit details:
Commit details:
${{ steps.head_update.outputs.commit_details }}
EOF
)"
# Push branch
git push -u origin "$BRANCH"
# Build PR body using heredoc with single-quoted delimiter to prevent
# bash from interpreting backticks in the markdown content as command
# substitution. GitHub Actions expressions are expanded before bash
# processes the script, so they still get substituted.
BASE_BODY=$(cat <<'PRBODY'
Changed files:
${{ steps.head_update.outputs.changed_files }}
Commit details:
${{ steps.head_update.outputs.commit_details }}
PRBODY
)
# Add intervention warning if needed
if [ "${{ steps.head_update.outputs.needs_manual_intervention }}" = "true" ]; then
INTERVENTION=$(cat <<'PRBODY'
## :warning: Manual Intervention Required
The following files were added or deleted and may require manual CMake configuration updates:
${{ steps.head_update.outputs.intervention_details }}
### Instructions
- **New C++ sources**: Add to appropriate list in `src/setup-ghidra-source.cmake`
- **Deleted C++ sources**: Remove from `src/setup-ghidra-source.cmake`
- **New spec files**: Review if `.slaspec` files are auto-generated; other types may need manual updates
- **Deleted spec files**: Verify no longer referenced
---
PRBODY
)
PR_BODY="${INTERVENTION}${BASE_BODY}"
else
PR_BODY="$BASE_BODY"
fi
# Create PR
gh pr create \
--title "Update Ghidra HEAD to commit ${{ steps.head_update.outputs.short_sha }}" \
--body "$PR_BODY"
${{ steps.head_update.outputs.commit_details }}
branch: cron/update-ghidra-${{ steps.head_update.outputs.short_sha }}
delete-branch: true
token: ${{ steps.generate-token.outputs.token }}
-117
View File
@@ -1,117 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Sleigh is a CMake-based build system for Ghidra's Sleigh/decompiler C++ libraries (by Trail of Bits). It wraps NSA's Ghidra source code — fetched via CMake FetchContent during configure — so the Sleigh disassembly and decompilation engines can be built as standalone C++ libraries for reuse outside Ghidra.
**The actual Ghidra C++ source is not in this repo.** It is cloned from GitHub automatically during CMake configuration.
## Build Commands
```sh
# Standard build (fetches Ghidra source on first configure)
cmake -B build -S .
cmake --build build -j$(nproc)
# CI-style build with developer mode (enables tests, docs, warnings)
cmake --preset=ci-ubuntu
cmake --build build -j$(nproc)
# Build with HEAD (bleeding-edge) Ghidra instead of stable
cmake -B build -S . -Dsleigh_RELEASE_TYPE=HEAD
# Use a local Ghidra checkout (avoids re-cloning)
cmake -B build -S . -Dsleigh_RELEASE_TYPE=HEAD \
-DFETCHCONTENT_SOURCE_DIR_GHIDRASOURCE=/path/to/ghidra
# Sanitizer build
cmake --preset=ci-sanitize
cmake --build build/sanitize -j$(nproc)
```
## Running Tests
Tests require `sleigh_DEVELOPER_MODE=ON` (enabled by all `ci-*` presets).
```sh
# Run all tests
cd build && ctest -VV
# Run specific test by name
cd build && ctest -VV -R sleigh_decomp_unittest # unit tests
cd build && ctest -VV -R sleigh_decomp_datatest # data-driven tests
cd build && ctest -VV -R sleigh_namespace_std_test # header hygiene check
```
Test binary: `sleigh_decomp_test` — wraps Ghidra's own test suite. Takes `-sleighpath` for compiled .sla files and a test mode (`unittests` or `datatests`).
## Architecture
### Library Targets
| Target | Library | C++ Std | Description |
|--------|---------|---------|-------------|
| `sleigh::sla` | `sla` | C++11 | Core sleigh: disassembly, encoding, p-code, emulation |
| `sleigh::decomp` | `decomp` | C++11 | Full decompiler (superset of sla) |
| `sleigh::support` | `slaSupport` | C++17 | Trail of Bits helpers: `FindSpecFile()`, version info |
Headers: `<ghidra/*.hh>` for upstream Ghidra headers, `<sleigh/*.h>` for ToB additions.
### Key Directories
- `src/setup-ghidra-source.cmake` — Pinned Ghidra versions, FetchContent, source file lists, patch application. **This is the central file for version bumps and source management.**
- `src/patches/{stable,HEAD}/` — Git-format patches applied to Ghidra source during fetch
- `src/spec_files_{stable,HEAD}.cmake` — Lists of ~148 .slaspec files to compile
- `tools/` — Ghidra executables (sleigh compiler, decompiler, ghidra service)
- `support/` — Trail of Bits support library (C++17)
- `extra-tools/sleigh-lift/` — Demo tool for disassembly/p-code lifting
- `cmake/modules/sleighCompile.cmake``sleigh_compile()` function exported for downstream users
### How Ghidra Source Integration Works
1. `src/setup-ghidra-source.cmake` pins a Ghidra git ref (tag for stable, commit hash for HEAD)
2. CMake FetchContent clones from `github.com/NationalSecurityAgency/ghidra`
3. Patches from `src/patches/` are applied via `git am`
4. Source file lists reference into `Ghidra/Features/Decompiler/src/decompile/cpp/`
5. Headers are copied into `build/include/ghidra/` for clean include paths
### Two Release Tracks
- **stable** (default): Pinned to a Ghidra release tag (e.g., `Ghidra_12.0.3_build`). Shallow clone.
- **HEAD**: Pinned to a specific commit on Ghidra main. Full clone. Updated weekly by CI via `scripts/update_ghidra_head.py`.
## Patch System
Patches fix upstream Ghidra bugs (UB sanitizer issues, portability, strict weak ordering violations) without changing Sleigh functionality. They are standard `git format-patch` files.
- `src/patches/stable/` — Patches for stable release
- `src/patches/HEAD/` — Superset of stable patches plus HEAD-specific fixes
- Custom patches via: `-Dsleigh_ADDITIONAL_PATCHES="path/to/patch1;path/to/patch2"`
When adding patches: create with `git format-patch`, number sequentially, and add to both directories if applicable. Patches in HEAD must be a superset of stable patches.
## Key Gotchas
- **In-source builds are forbidden** — enforced by `cmake/prelude.cmake`
- **First configure is slow** — Ghidra repo clone takes time (especially HEAD which can't shallow clone)
- **Core libraries are C++11** — the support library and extra tools are C++17, but `sleigh::sla` and `sleigh::decomp` must stay C++11
- **Source file lists are manual** — when Ghidra adds/removes .cc files, `src/setup-ghidra-source.cmake` must be updated; the `scripts/update_ghidra_head.py` script detects new files and flags them
- **Strict weak ordering** — a recurring class of upstream bugs in Ghidra comparators; several patches fix these
- **Spec files differ between releases** — `src/spec_files_stable.cmake` and `src/spec_files_HEAD.cmake` are separate lists and must be maintained independently
## CMake Presets Reference
| Preset | Use Case |
|--------|----------|
| `ci-ubuntu` | Linux dev build with warnings and tests |
| `ci-macos` | macOS dev build |
| `ci-windows` | Windows dev build (VS 2022, vcpkg) |
| `ci-sanitize` | ASan + UBSan (builds to `build/sanitize/`) |
| `ci-coverage` | Code coverage (builds to `build/coverage/`) |
## Code Style
- `.clang-format`: BasedOnStyle LLVM
- Compiler warnings are strict in CI (see `flags-unix`/`flags-windows` presets)
+4 -6
View File
@@ -147,13 +147,11 @@ set(public_include_header_list
"${library_root}/slaformat.hh"
"${library_root}/constseq.hh"
"${library_root}/expression.hh"
"${library_root}/bitfield.hh"
)
# if(sleigh_RELEASE_IS_HEAD)
# list(APPEND public_include_header_list
# )
# endif()
#if(sleigh_RELEASE_IS_HEAD)
# list(APPEND public_include_header_list
# )
#endif()
# Create custom target so that IDEs know these files are part of the sources
add_custom_target(sleigh_all_headers SOURCES ${public_include_header_list})
set(public_headers_dir ${CMAKE_CURRENT_BINARY_DIR}/include)
+1 -12
View File
@@ -77,7 +77,7 @@
{
"name": "ci-win64",
"inherits": ["flags-windows", "ci-std"],
"generator": "Visual Studio 18 2026",
"generator": "Visual Studio 17 2022",
"architecture": "x64",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake",
@@ -114,17 +114,6 @@
"CMAKE_CXX_FLAGS_SANITIZE": "-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common"
}
},
{
"name": "ci-assertions",
"binaryDir": "${sourceDir}/build/assertions",
"inherits": ["ci-unix", "dev-mode"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Assertions",
"CMAKE_CXX_FLAGS_ASSERTIONS": "-O0 -g -stdlib=libc++ -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG -fstack-protector-strong",
"CMAKE_EXE_LINKER_FLAGS_ASSERTIONS": "-stdlib=libc++ -lc++abi",
"CMAKE_SHARED_LINKER_FLAGS_ASSERTIONS": "-stdlib=libc++ -lc++abi"
}
},
{
"name": "ci-build",
"binaryDir": "${sourceDir}/build",
+1
View File
@@ -29,6 +29,7 @@ set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedd
set(CMAKE_VS_GLOBALS
"CLToolExe=cl.exe"
"CLToolPath=${CMAKE_BINARY_DIR}"
"TrackFileAccess=false"
"UseMultiToolTask=true"
"DebugInformationFormat=OldStyle"
)
+15 -210
View File
@@ -8,7 +8,6 @@ import shutil
import subprocess
import sys
import tempfile
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Dict, Optional, Any, Tuple
@@ -24,92 +23,12 @@ SLEIGH_PATHS = [
"Ghidra/Processors", # Sleigh files
]
# File extensions requiring manual CMake intervention
CPP_EXTENSIONS = {".cc", ".hh"}
SPEC_EXTENSIONS = {".slaspec", ".cspec", ".pspec", ".ldefs", ".opinion", ".sinc"}
# File extensions to ignore (in addition to .java)
IGNORED_EXTENSIONS = {
".java",
".gradle",
".properties",
".txt",
".md",
".html",
".xml",
".png",
".gif",
".jpg",
".ico",
}
# Paths for categorizing files
CPP_PATH = "Ghidra/Features/Decompiler/src/decompile/cpp/"
SPEC_PATH_PREFIX = "Ghidra/Processors/"
# Regex patterns
HEAD_COMMIT_PATTERN = r"set\(ghidra_head_git_tag \"([0-9A-Fa-f]+)\"\)"
VERSION_PATTERN = r"set\(ghidra_head_version \"([0-9]+(\.[0-9]+)*)\"\)"
APP_VERSION_PATTERN = r"application.version=([0-9]+(\.[0-9]+)*)"
@dataclass
class CategorizedChanges:
"""Holds files categorized by change type and file type."""
added_cpp: List[str] = field(default_factory=list)
deleted_cpp: List[str] = field(default_factory=list)
added_spec: List[str] = field(default_factory=list)
deleted_spec: List[str] = field(default_factory=list)
def needs_manual_intervention(self) -> bool:
"""Check if any files need manual intervention."""
return bool(
self.added_cpp or self.deleted_cpp or self.added_spec or self.deleted_spec
)
def format_intervention_details(self) -> str:
"""Format the intervention details as markdown."""
sections = []
if self.added_cpp:
sections.append("### New C++ Source Files")
sections.append(
"These files need to be added to `src/setup-ghidra-source.cmake`:"
)
for f in self.added_cpp:
sections.append(f"- `{f}`")
sections.append("")
if self.deleted_cpp:
sections.append("### Deleted C++ Source Files")
sections.append(
"These files need to be removed from `src/setup-ghidra-source.cmake`:"
)
for f in self.deleted_cpp:
sections.append(f"- `{f}`")
sections.append("")
if self.added_spec:
sections.append("### New Spec Files")
sections.append(
"Review if these files need manual CMake updates (`.slaspec` files "
"are auto-generated; other types may need manual updates):"
)
for f in self.added_spec:
sections.append(f"- `{f}`")
sections.append("")
if self.deleted_spec:
sections.append("### Deleted Spec Files")
sections.append("Verify these files are no longer referenced:")
for f in self.deleted_spec:
sections.append(f"- `{f}`")
sections.append("")
return "\n".join(sections).rstrip()
class GitHelper:
"""Helper class for Git operations"""
@@ -151,60 +70,6 @@ class GitHelper:
except subprocess.CalledProcessError:
return False
@staticmethod
def _should_ignore_file(file_path: str) -> bool:
"""Check if a file should be ignored based on its extension."""
ext = Path(file_path).suffix.lower()
return ext in IGNORED_EXTENSIONS
@staticmethod
def _categorize_file(
status: str, file_path: str, categorized: CategorizedChanges
) -> None:
"""Categorize a file based on its status and type.
Args:
status: Git status code (A, D, M, R, etc.)
file_path: Path to the file
categorized: CategorizedChanges object to update
"""
ext = Path(file_path).suffix.lower()
# Only categorize added (A) or deleted (D) files
if status not in ("A", "D"):
return
# Check if it's a C++ file in the decompiler path
if ext in CPP_EXTENSIONS and CPP_PATH in file_path:
if status == "A":
categorized.added_cpp.append(file_path)
else: # status == "D"
categorized.deleted_cpp.append(file_path)
return
# Check if it's a spec file in the Processors path
if ext in SPEC_EXTENSIONS and file_path.startswith(SPEC_PATH_PREFIX):
if status == "A":
categorized.added_spec.append(file_path)
else: # status == "D"
categorized.deleted_spec.append(file_path)
@staticmethod
def _parse_git_status_line(line: str) -> Tuple[str, str, Optional[str]]:
"""Parse a git status line from --name-status output.
Returns:
Tuple of (status, file_path, new_path_for_rename)
"""
parts = line.split("\t")
status = parts[0]
# Handle rename (R100 or similar)
if status.startswith("R"):
return ("R", parts[1], parts[2])
return (status, parts[1], None)
def get_commit_info(
self, repo_dir: Path, old_commit: str, new_commit: str, paths: List[str]
) -> List[Dict[str, Any]]:
@@ -254,21 +119,8 @@ class GitHelper:
)
commit_files = files_result.stdout.strip().splitlines()
# Filter out ignored files
filtered_files = []
for line in commit_files:
if not line.strip():
continue
status, file_path, new_path = self._parse_git_status_line(line)
if status == "R":
# For renames, check both old and new paths
if not self._should_ignore_file(file_path):
filtered_files.append(f"D\t{file_path}")
if new_path and not self._should_ignore_file(new_path):
filtered_files.append(f"A\t{new_path}")
elif not self._should_ignore_file(file_path):
filtered_files.append(line)
commit_files = filtered_files
# Filter out Java files
commit_files = [f for f in commit_files if not f.endswith(".java")]
if commit_files:
commits.append(
@@ -285,12 +137,8 @@ class GitHelper:
def get_changed_files(
self, repo_dir: Path, old_commit: str, new_commit: str, paths: List[str]
) -> Tuple[List[str], CategorizedChanges]:
"""Get list of files changed between commits and categorize them.
Returns:
Tuple of (filtered_files_list, categorized_changes)
"""
) -> List[str]:
"""Get list of files changed between commits"""
result = self.run(
[
"diff",
@@ -303,29 +151,11 @@ class GitHelper:
capture_output=True,
)
raw_lines = result.stdout.strip().splitlines()
filtered_files = []
categorized = CategorizedChanges()
changed_files = result.stdout.strip().splitlines()
# Filter out Java files
changed_files = [f for f in changed_files if not f.endswith(".java")]
for line in raw_lines:
if not line.strip():
continue
status, file_path, new_path = self._parse_git_status_line(line)
if status == "R":
# For renames, treat as delete old + add new
if not self._should_ignore_file(file_path):
filtered_files.append(f"D\t{file_path}")
self._categorize_file("D", file_path, categorized)
if new_path and not self._should_ignore_file(new_path):
filtered_files.append(f"A\t{new_path}")
self._categorize_file("A", new_path, categorized)
elif not self._should_ignore_file(file_path):
filtered_files.append(line)
self._categorize_file(status, file_path, categorized)
return filtered_files, categorized
return changed_files
class GhidraUpdater:
@@ -375,20 +205,16 @@ class GhidraUpdater:
def display_changes(
self, repo_dir: Path, start_commit: str, end_commit: str
) -> Tuple[List[str], List[Dict[str, Any]], CategorizedChanges]:
"""Display changes between two commits and return the changed files and commit info.
Returns:
Tuple of (changed_files, commit_info, categorized_changes)
"""
# Get changed files and categorized changes
changed_files, categorized = self.git.get_changed_files(
) -> Tuple[List[str], List[Dict[str, Any]]]:
"""Display changes between two commits and return the changed files and commit info"""
# Get changed files
changed_files = self.git.get_changed_files(
repo_dir, start_commit, end_commit, SLEIGH_PATHS
)
if not changed_files:
print("No sleigh files were modified between these commits")
return [], [], CategorizedChanges()
return [], []
# Output changes for logging
num_changed = len(changed_files)
@@ -396,18 +222,6 @@ class GhidraUpdater:
for file in changed_files:
print(f" {file}")
# Display manual intervention warning if needed
if categorized.needs_manual_intervention():
print("\n** Manual intervention may be required **")
if categorized.added_cpp:
print(f" New C++ files: {len(categorized.added_cpp)}")
if categorized.deleted_cpp:
print(f" Deleted C++ files: {len(categorized.deleted_cpp)}")
if categorized.added_spec:
print(f" New spec files: {len(categorized.added_spec)}")
if categorized.deleted_spec:
print(f" Deleted spec files: {len(categorized.deleted_spec)}")
# Get detailed commit info for logging
commit_info = self.git.get_commit_info(
repo_dir, start_commit, end_commit, SLEIGH_PATHS
@@ -455,16 +269,7 @@ class GhidraUpdater:
self.log_github_multiline_output("commit_details", "\n".join(details))
# Log manual intervention outputs
if categorized.needs_manual_intervention():
self.log_github_output("needs_manual_intervention", "true")
self.log_github_multiline_output(
"intervention_details", categorized.format_intervention_details()
)
else:
self.log_github_output("needs_manual_intervention", "false")
return changed_files, commit_info, categorized
return changed_files, commit_info
def update_head_commit(
self, repo_dir: Path, setup_file: Path
@@ -493,7 +298,7 @@ class GhidraUpdater:
print(f"Found new commit: {latest_commit}")
# Check if sleigh files were updated and display changes
changed_files, commit_info, _ = self.display_changes(
changed_files, commit_info = self.display_changes(
repo_dir, current_commit, latest_commit
)
@@ -1,7 +1,7 @@
From 98769d3cd2f417a52559bf68700e2f9f3e7a0e4f Mon Sep 17 00:00:00 2001
From 9d1e7b00e8f5dca987038a78fbac400c835a78be Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Mon, 12 Aug 2024 12:02:35 -0400
Subject: [PATCH 1/7] Fix UBSAN errors in decompiler
Subject: [PATCH 1/5] Fix UBSAN errors in decompiler
Co-authored-by: Alex Cameron <asc@tetsuo.sh>
---
@@ -13,10 +13,10 @@ Co-authored-by: Alex Cameron <asc@tetsuo.sh>
5 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
index a67a3de849..37ba4930e6 100644
index ca9d71ab99..85d4dd281d 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
@@ -618,8 +618,10 @@ vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin
@@ -621,8 +621,10 @@ vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin
uint4 smallsize = (numbits+7)/8; // Size of input (output of rhs)
bool shiftneeded = (bitoffset != 0);
bool zextneeded = true;
@@ -29,7 +29,7 @@ index a67a3de849..37ba4930e6 100644
if (vn->getSize().getType()==ConstTpl::real) {
// If we know the size of the bitranged varnode, we can
@@ -723,9 +725,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
@@ -726,9 +728,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
}
}
@@ -39,7 +39,7 @@ index a67a3de849..37ba4930e6 100644
if (truncneeded && ((bitoffset % 8)==0)) {
truncshift = bitoffset/8;
bitoffset = 0;
@@ -748,8 +747,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
@@ -751,8 +750,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
appendOp(CPUI_INT_RIGHT,res,bitoffset,4);
if (truncneeded)
appendOp(CPUI_SUBPIECE,res,truncshift,4);
@@ -56,7 +56,7 @@ index a67a3de849..37ba4930e6 100644
return res;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
index 18e2ff8ba1..3bfe29f2ef 100644
index cd9b9835b1..8a4616c3b9 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
@@ -22,6 +22,7 @@ ConstTpl::ConstTpl(const_type tp)
@@ -76,7 +76,7 @@ index 18e2ff8ba1..3bfe29f2ef 100644
bool ConstTpl::isConstSpace(void) const
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh
index b53b18797d..b2f043e32d 100644
index e0b069959d..9117a45c75 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh
@@ -47,7 +47,7 @@ class ConstTpl {
@@ -89,10 +89,10 @@ index b53b18797d..b2f043e32d 100644
type=op2.type; value=op2.value; value_real=op2.value_real; select=op2.select; }
ConstTpl(const_type tp,uintb val);
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
index 75bebffcb0..bf5e7ce681 100644
index 50d85e22ba..9f3b456229 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
@@ -2323,8 +2323,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
@@ -2164,8 +2164,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
ostringstream msg;
SymbolTree::const_iterator iter;
for(iter=scope->begin();iter!=scope->end();++iter) {
@@ -116,5 +116,5 @@ index 2571f55f1a..fe40e22b1b 100644
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
uintb encoding = format.getEncoding(f);
--
2.51.1
2.50.1
@@ -1,7 +1,7 @@
From 4e6f277eae589c4f9bceb40337d43384248d272c Mon Sep 17 00:00:00 2001
From 729f72060849dc4f29e89b1c76a980563ffd3e2a Mon Sep 17 00:00:00 2001
From: Alex Cameron <asc@tetsuo.sh>
Date: Wed, 3 Aug 2022 20:01:18 +1000
Subject: [PATCH 2/7] Use `stroull` instead of `stroul` to parse address
Subject: [PATCH 2/5] Use `stroull` instead of `stroul` to parse address
offsets
---
@@ -34,5 +34,5 @@ index dbaa2e775f..72927bf379 100644
enddata = (const char *) tmpdata;
if (enddata - s.c_str() == s.size()) { // If no size or offset override
--
2.51.1
2.50.1
@@ -1,7 +1,7 @@
From 41eb6be342d29d6ff618d06b391bf33209fe8ade Mon Sep 17 00:00:00 2001
From 39cfff6f08dad8a85f992e09b3e26716c9173bf7 Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Tue, 29 Oct 2024 17:51:09 -0400
Subject: [PATCH 3/7] Ignore floating point test due to compilation differences
Subject: [PATCH 3/5] Ignore floating point test due to compilation differences
This test fails on macOS and Windows. I'm unsure whether it's an OS or
compiler issue.
@@ -24,5 +24,5 @@ index fe40e22b1b..91440e2510 100644
ASSERT_EQUALS(ff.printDecimal(f2, false), "0.33333334");
double f3 = doubleFromRawBits(0x3fd0000000000000);
--
2.51.1
2.50.1
@@ -1,7 +1,7 @@
From 8aa8bf7a146ab43f5b1b27386667af52269720fd Mon Sep 17 00:00:00 2001
From 2aa4395ef8874ee9890126f4bdad0d71adf9eacc Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Wed, 30 Oct 2024 14:26:57 -0400
Subject: [PATCH 4/7] Allow positive or negative NAN in decompiler floating
Subject: [PATCH 4/5] Allow positive or negative NAN in decompiler floating
point test
At least on Apple Silicon, this test reports positive NAN.
@@ -33,5 +33,5 @@ index f8108d3d32..1060a3e193 100644
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.1415926535897933e-06;</stringmatch>
</decompilertest>
--
2.51.1
2.50.1
@@ -1,7 +1,7 @@
From d9242056b580cc994661c41344c9ad51b636b0e5 Mon Sep 17 00:00:00 2001
From 2a6bd0a0ad7797160db887bba7137b77aa148ba5 Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Sat, 8 Feb 2025 17:59:57 -0500
Subject: [PATCH 5/7] decompiler: Fix strict weak ordering TypePartialEnum
Subject: [PATCH 5/5] decompiler: Fix strict weak ordering TypePartialEnum
This fixes Windows Debug error encountered in testing where it was
complaining about lack of strict weak ordering.
@@ -10,10 +10,10 @@ complaining about lack of strict weak ordering.
1 file changed, 1 insertion(+)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
index 2d48650e70..4a52633ebf 100644
index 962c525b7f..7db5024b54 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
@@ -2703,6 +2703,7 @@ int4 TypePartialEnum::compareDependency(const Datatype &op) const
@@ -2303,6 +2303,7 @@ int4 TypePartialEnum::compareDependency(const Datatype &op) const
{
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
@@ -22,5 +22,5 @@ index 2d48650e70..4a52633ebf 100644
if (parent != tp->parent) return (parent < tp->parent) ? -1 : 1; // Compare absolute pointers
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
--
2.51.1
2.50.1
@@ -1,29 +0,0 @@
From f2fdafbd981a441c35ab243bcb74872febd6bbd4 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 02:29:10 +0000
Subject: [PATCH 6/7] decompiler: Fix strict weak ordering PullRecord
When both readOp pointers are null, PullRecord::operator< incorrectly
returns true, violating strict weak ordering (irreflexivity). This is
caught by libc++ debug mode hardening assertions at pullList.sort().
---
Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc
index add61c741b..3e01d2e045 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc
@@ -1058,7 +1058,7 @@ bool BitFieldPullTransform::PullRecord::operator<(const PullRecord &op2) const
return (readOp->getSeqNum() < op2.readOp->getSeqNum());
}
else if (readOp == (PcodeOp *)0)
- return true;
+ return (op2.readOp != (PcodeOp *)0);
else if (op2.readOp == (PcodeOp *)0)
return false;
return false;
--
2.51.1
@@ -1,29 +0,0 @@
From a44beeb3a2569887c33d2dfd509c0d103bd6c447 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 02:43:17 +0000
Subject: [PATCH 7/7] decompiler: Fix strict weak ordering compareFinalOrder
FlowBlock::compareFinalOrder returns true when both blocks have index 0,
violating strict weak ordering (irreflexivity). This is caught by libc++
debug mode hardening assertions during BlockGraph::orderBlocks sort.
---
Ghidra/Features/Decompiler/src/decompile/cpp/block.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
index bf7103d916..2e495d9c3f 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
@@ -709,7 +709,7 @@ string FlowBlock::typeToName(FlowBlock::block_type bt)
bool FlowBlock::compareFinalOrder(const FlowBlock *bl1,const FlowBlock *bl2)
{
- if (bl1->getIndex() == 0) return true; // Make sure the entry point comes first
+ if (bl1->getIndex() == 0) return (bl2->getIndex() != 0); // Make sure the entry point comes first
if (bl2->getIndex() == 0) return false;
PcodeOp *op1 = bl1->lastOp();
PcodeOp *op2 = bl2->lastOp();
--
2.51.1
@@ -1,57 +0,0 @@
From 8afa7d89a368b358a5493b87804985ed9ac70a2f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 20:41:10 +0000
Subject: [PATCH] Fix UBSAN signed left shift errors
Cast signed values to unsigned before left shifting to avoid undefined
behavior when the signed value is negative or when the result cannot be
represented in the signed type.
Fixes runtime errors detected by UndefinedBehaviorSanitizer:
- address.hh sign_extend(): left shift of negative value
- address.hh sign_extend(): left shift cannot be represented in type 'intb'
- slghpatexpress.cc LeftShiftExpression: left shift of negative value
---
Ghidra/Features/Decompiler/src/decompile/cpp/address.hh | 2 +-
.../Features/Decompiler/src/decompile/cpp/slghpatexpress.cc | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
index 45144daf3..0e75c68b8 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
@@ -544,7 +544,7 @@ inline intb sign_extend(intb val,int4 bit)
{
int4 sa = 8*sizeof(intb) - (bit+1);
- val = (val << sa) >> sa;
+ val = static_cast<intb>(static_cast<uintb>(val) << sa) >> sa;
return val;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
index 941097859..93cce378a 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
@@ -969,7 +969,7 @@ intb LeftShiftExpression::getValue(ParserWalker &walker) const
{
intb leftval = getLeft()->getValue(walker);
intb rightval = getRight()->getValue(walker);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos) const
@@ -977,7 +977,7 @@ intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos)
{
intb leftval = getLeft()->getSubValue(replace,listpos); // Must be left first
intb rightval = getRight()->getSubValue(replace,listpos);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
void LeftShiftExpression::encode(Encoder &encoder) const
--
2.51.1
@@ -1,49 +0,0 @@
From eee198c1cc17bfc8b10609ddc8e03e254d0bac9a Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.kilmer@trailofbits.com>
Date: Tue, 21 Apr 2026 10:32:17 -0400
Subject: [PATCH] Use std::stoull instead of std::stoul in scan_number for MSVC
std::stoul returns `unsigned long`, which is 32 bits on MSVC (LLP64) but 64
bits on POSIX LP64. scan_number assigns the result into a `uintb` (uint64_t),
so on Windows any integer literal in a .slaspec file that exceeds 0xFFFFFFFF
throws std::out_of_range, the catch-all returns BADINTEGER, and the sleigh
compiler rejects the spec. Affected specs include 68000/PowerPC/Loongarch/
coldfire/avr32a and others that use 64-bit immediates.
std::stoull returns `unsigned long long` (64 bits on all platforms), matching
the uintb target and the pre-GP-6608 behavior. This mirrors the earlier fix
applied to AddrSpace::read in space.cc.
---
Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.cc | 2 +-
Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.l | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.cc
index f3c1e361..d0217389 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.cc
@@ -1582,7 +1582,7 @@ int4 scan_number(char *numtext,SLEIGHSTYPE *lval,int4 radix,bool signednum)
{
uintb val;
try {
- val = std::stoul(numtext,(size_t *)0,radix);
+ val = std::stoull(numtext,(size_t *)0,radix);
}
catch(...) {
return BADINTEGER;
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.l b/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.l
index 1c25962a..29db987b 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.l
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.l
@@ -461,7 +461,7 @@ int4 scan_number(char *numtext,SLEIGHSTYPE *lval,int4 radix,bool signednum)
{
uintb val;
try {
- val = std::stoul(numtext,(size_t *)0,radix);
+ val = std::stoull(numtext,(size_t *)0,radix);
}
catch(...) {
return BADINTEGER;
--
2.54.0
@@ -1,7 +1,7 @@
From 31a52c1f29a0fe67956bbefc1b1b01d6c0ccd5b1 Mon Sep 17 00:00:00 2001
From 9d1e7b00e8f5dca987038a78fbac400c835a78be Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Mon, 12 Aug 2024 12:02:35 -0400
Subject: [PATCH 1/8] Fix UBSAN errors in decompiler
Subject: [PATCH 1/5] Fix UBSAN errors in decompiler
Co-authored-by: Alex Cameron <asc@tetsuo.sh>
---
@@ -13,10 +13,10 @@ Co-authored-by: Alex Cameron <asc@tetsuo.sh>
5 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
index a67a3de849..37ba4930e6 100644
index ca9d71ab99..85d4dd281d 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
@@ -618,8 +618,10 @@ vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin
@@ -621,8 +621,10 @@ vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin
uint4 smallsize = (numbits+7)/8; // Size of input (output of rhs)
bool shiftneeded = (bitoffset != 0);
bool zextneeded = true;
@@ -29,7 +29,7 @@ index a67a3de849..37ba4930e6 100644
if (vn->getSize().getType()==ConstTpl::real) {
// If we know the size of the bitranged varnode, we can
@@ -723,9 +725,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
@@ -726,9 +728,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
}
}
@@ -39,7 +39,7 @@ index a67a3de849..37ba4930e6 100644
if (truncneeded && ((bitoffset % 8)==0)) {
truncshift = bitoffset/8;
bitoffset = 0;
@@ -748,8 +747,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
@@ -751,8 +750,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
appendOp(CPUI_INT_RIGHT,res,bitoffset,4);
if (truncneeded)
appendOp(CPUI_SUBPIECE,res,truncshift,4);
@@ -56,7 +56,7 @@ index a67a3de849..37ba4930e6 100644
return res;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
index 18e2ff8ba1..3bfe29f2ef 100644
index cd9b9835b1..8a4616c3b9 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
@@ -22,6 +22,7 @@ ConstTpl::ConstTpl(const_type tp)
@@ -76,7 +76,7 @@ index 18e2ff8ba1..3bfe29f2ef 100644
bool ConstTpl::isConstSpace(void) const
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh
index b53b18797d..b2f043e32d 100644
index e0b069959d..9117a45c75 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh
@@ -47,7 +47,7 @@ class ConstTpl {
@@ -89,10 +89,10 @@ index b53b18797d..b2f043e32d 100644
type=op2.type; value=op2.value; value_real=op2.value_real; select=op2.select; }
ConstTpl(const_type tp,uintb val);
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
index 75bebffcb0..bf5e7ce681 100644
index 50d85e22ba..9f3b456229 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
@@ -2323,8 +2323,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
@@ -2164,8 +2164,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
ostringstream msg;
SymbolTree::const_iterator iter;
for(iter=scope->begin();iter!=scope->end();++iter) {
@@ -116,5 +116,5 @@ index 2571f55f1a..fe40e22b1b 100644
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
uintb encoding = format.getEncoding(f);
--
2.54.0
2.50.1
@@ -1,7 +1,7 @@
From 0928d2c8e97646042265cee1d2752514d02d3d4f Mon Sep 17 00:00:00 2001
From 729f72060849dc4f29e89b1c76a980563ffd3e2a Mon Sep 17 00:00:00 2001
From: Alex Cameron <asc@tetsuo.sh>
Date: Wed, 3 Aug 2022 20:01:18 +1000
Subject: [PATCH 2/8] Use `stroull` instead of `stroul` to parse address
Subject: [PATCH 2/5] Use `stroull` instead of `stroul` to parse address
offsets
---
@@ -34,5 +34,5 @@ index dbaa2e775f..72927bf379 100644
enddata = (const char *) tmpdata;
if (enddata - s.c_str() == s.size()) { // If no size or offset override
--
2.54.0
2.50.1
@@ -1,7 +1,7 @@
From e08c2cb8c6f49a2127b14ba081f1a5ec600c3cea Mon Sep 17 00:00:00 2001
From 39cfff6f08dad8a85f992e09b3e26716c9173bf7 Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Tue, 29 Oct 2024 17:51:09 -0400
Subject: [PATCH 3/8] Ignore floating point test due to compilation differences
Subject: [PATCH 3/5] Ignore floating point test due to compilation differences
This test fails on macOS and Windows. I'm unsure whether it's an OS or
compiler issue.
@@ -24,5 +24,5 @@ index fe40e22b1b..91440e2510 100644
ASSERT_EQUALS(ff.printDecimal(f2, false), "0.33333334");
double f3 = doubleFromRawBits(0x3fd0000000000000);
--
2.54.0
2.50.1
@@ -1,7 +1,7 @@
From b5500258acdee1b2f5dfe603b1da33a7eebf699f Mon Sep 17 00:00:00 2001
From 2aa4395ef8874ee9890126f4bdad0d71adf9eacc Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Wed, 30 Oct 2024 14:26:57 -0400
Subject: [PATCH 4/8] Allow positive or negative NAN in decompiler floating
Subject: [PATCH 4/5] Allow positive or negative NAN in decompiler floating
point test
At least on Apple Silicon, this test reports positive NAN.
@@ -33,5 +33,5 @@ index f8108d3d32..1060a3e193 100644
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.1415926535897933e-06;</stringmatch>
</decompilertest>
--
2.54.0
2.50.1
@@ -1,7 +1,7 @@
From 6d5dbe6ffcfc31468ebbf2299ea72f5ef8abcf15 Mon Sep 17 00:00:00 2001
From 2a6bd0a0ad7797160db887bba7137b77aa148ba5 Mon Sep 17 00:00:00 2001
From: Eric Kilmer <eric.d.kilmer@gmail.com>
Date: Sat, 8 Feb 2025 17:59:57 -0500
Subject: [PATCH 5/8] decompiler: Fix strict weak ordering TypePartialEnum
Subject: [PATCH 5/5] decompiler: Fix strict weak ordering TypePartialEnum
This fixes Windows Debug error encountered in testing where it was
complaining about lack of strict weak ordering.
@@ -10,10 +10,10 @@ complaining about lack of strict weak ordering.
1 file changed, 1 insertion(+)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
index e3601d3a99..fcc7b3f250 100644
index 962c525b7f..7db5024b54 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
@@ -2704,6 +2704,7 @@ int4 TypePartialEnum::compareDependency(const Datatype &op) const
@@ -2303,6 +2303,7 @@ int4 TypePartialEnum::compareDependency(const Datatype &op) const
{
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
@@ -22,5 +22,5 @@ index e3601d3a99..fcc7b3f250 100644
if (parent != tp->parent) return (parent < tp->parent) ? -1 : 1; // Compare absolute pointers
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
--
2.54.0
2.50.1
@@ -1,29 +0,0 @@
From 4fba77dafc00c6a359b21b94be1be8d24db37d39 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 02:29:10 +0000
Subject: [PATCH 6/8] decompiler: Fix strict weak ordering PullRecord
When both readOp pointers are null, PullRecord::operator< incorrectly
returns true, violating strict weak ordering (irreflexivity). This is
caught by libc++ debug mode hardening assertions at pullList.sort().
---
Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc
index add61c741b..3e01d2e045 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/bitfield.cc
@@ -1058,7 +1058,7 @@ bool BitFieldPullTransform::PullRecord::operator<(const PullRecord &op2) const
return (readOp->getSeqNum() < op2.readOp->getSeqNum());
}
else if (readOp == (PcodeOp *)0)
- return true;
+ return (op2.readOp != (PcodeOp *)0);
else if (op2.readOp == (PcodeOp *)0)
return false;
return false;
--
2.54.0
@@ -1,29 +0,0 @@
From 60f88f2a93a5b9c7a5a2d6b73b100cf353a6982c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 02:43:17 +0000
Subject: [PATCH 7/8] decompiler: Fix strict weak ordering compareFinalOrder
FlowBlock::compareFinalOrder returns true when both blocks have index 0,
violating strict weak ordering (irreflexivity). This is caught by libc++
debug mode hardening assertions during BlockGraph::orderBlocks sort.
---
Ghidra/Features/Decompiler/src/decompile/cpp/block.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
index bf7103d916..2e495d9c3f 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
@@ -709,7 +709,7 @@ string FlowBlock::typeToName(FlowBlock::block_type bt)
bool FlowBlock::compareFinalOrder(const FlowBlock *bl1,const FlowBlock *bl2)
{
- if (bl1->getIndex() == 0) return true; // Make sure the entry point comes first
+ if (bl1->getIndex() == 0) return (bl2->getIndex() != 0); // Make sure the entry point comes first
if (bl2->getIndex() == 0) return false;
PcodeOp *op1 = bl1->lastOp();
PcodeOp *op2 = bl2->lastOp();
--
2.54.0
@@ -1,57 +0,0 @@
From 7da7356c3093390b99172a4adb1422e057243903 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 20:41:10 +0000
Subject: [PATCH 8/8] Fix UBSAN signed left shift errors
Cast signed values to unsigned before left shifting to avoid undefined
behavior when the signed value is negative or when the result cannot be
represented in the signed type.
Fixes runtime errors detected by UndefinedBehaviorSanitizer:
- address.hh sign_extend(): left shift of negative value
- address.hh sign_extend(): left shift cannot be represented in type 'intb'
- slghpatexpress.cc LeftShiftExpression: left shift of negative value
---
Ghidra/Features/Decompiler/src/decompile/cpp/address.hh | 2 +-
.../Features/Decompiler/src/decompile/cpp/slghpatexpress.cc | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
index 510432e5a2..0aaddea035 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
@@ -587,7 +587,7 @@ inline intb sign_extend(intb val,int4 bit)
{
int4 sa = 8*sizeof(intb) - (bit+1);
- val = (val << sa) >> sa;
+ val = static_cast<intb>(static_cast<uintb>(val) << sa) >> sa;
return val;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
index 9410978595..93cce378af 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
@@ -969,7 +969,7 @@ intb LeftShiftExpression::getValue(ParserWalker &walker) const
{
intb leftval = getLeft()->getValue(walker);
intb rightval = getRight()->getValue(walker);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos) const
@@ -977,7 +977,7 @@ intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos)
{
intb leftval = getLeft()->getSubValue(replace,listpos); // Must be left first
intb rightval = getRight()->getSubValue(replace,listpos);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
void LeftShiftExpression::encode(Encoder &encoder) const
--
2.54.0
+11 -25
View File
@@ -22,7 +22,7 @@ set_property(CACHE sleigh_RELEASE_TYPE PROPERTY STRINGS "stable" "HEAD")
find_package(Git REQUIRED)
# Ghidra pinned stable version commit
set(ghidra_version "12.1.2")
set(ghidra_version "12.0.1")
set(ghidra_git_tag "Ghidra_${ghidra_version}_build")
set(ghidra_shallow TRUE)
@@ -37,25 +37,21 @@ set(ghidra_patch_email "41898282+github-actions[bot]@users.noreply.github.com")
set(ghidra_patches
PATCH_COMMAND "${GIT_EXECUTABLE}" config user.name "${ghidra_patch_user}" &&
"${GIT_EXECUTABLE}" config user.email "${ghidra_patch_email}" &&
"${GIT_EXECUTABLE}" config core.longpaths true &&
"${GIT_EXECUTABLE}" am --ignore-space-change --ignore-whitespace --no-gpg-sign
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0001-Fix-UBSAN-errors-in-decompiler.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0002-Use-stroull-instead-of-stroul-to-parse-address-offse.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0003-Ignore-floating-point-test-due-to-compilation-differ.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0004-Allow-positive-or-negative-NAN-in-decompiler-floatin.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0005-decompiler-Fix-strict-weak-ordering-TypePartialEnum.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0006-decompiler-Fix-strict-weak-ordering-PullRecord.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0007-decompiler-Fix-strict-weak-ordering-compareFinalOrde.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0008-Fix-UBSAN-signed-left-shift-errors.patch"
)
# Ghidra pinned commits used for pinning last known working HEAD commit
if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
# TODO: Try to remember to look at Ghidra/application.properties
# TODO: CMake only likes numeric characters in the version string....
set(ghidra_head_version "12.2")
set(ghidra_head_version "12.1")
set(ghidra_version "${ghidra_head_version}")
set(ghidra_head_git_tag "74d498f8da7d13f84604f531d59cb0cac028d6b2")
set(ghidra_head_git_tag "549735683cce5bd6220a4355de123a825ebff328")
set(ghidra_git_tag "${ghidra_head_git_tag}")
set(ghidra_shallow FALSE)
set(ghidra_patches
@@ -67,10 +63,6 @@ if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0003-Ignore-floating-point-test-due-to-compilation-differ.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0004-Allow-positive-or-negative-NAN-in-decompiler-floatin.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0005-decompiler-Fix-strict-weak-ordering-TypePartialEnum.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0006-decompiler-Fix-strict-weak-ordering-PullRecord.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0007-decompiler-Fix-strict-weak-ordering-compareFinalOrde.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0008-Fix-UBSAN-signed-left-shift-errors.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0009-Use-std-stoull-instead-of-std-stoul-in-scan_number-f.patch"
)
string(SUBSTRING "${ghidra_git_tag}" 0 7 ghidra_short_commit)
else()
@@ -117,10 +109,10 @@ set(sleigh_core_source_list
"${library_root}/globalcontext.cc"
"${library_root}/marshal.cc"
)
# if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
# list(APPEND sleigh_core_source_list
# )
# endif()
#if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
# list(APPEND sleigh_core_source_list
# )
#endif()
set(sleigh_deccore_source_list
"${library_root}/capability.cc"
@@ -181,13 +173,11 @@ set(sleigh_deccore_source_list
"${library_root}/multiprecision.cc"
"${library_root}/constseq.cc"
"${library_root}/expression.cc"
"${library_root}/bitfield.cc"
)
# if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
# list(APPEND sleigh_deccore_source_list
# )
# endif()
#if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
# list(APPEND sleigh_deccore_source_list
# )
#endif()
set(sleigh_extra_source_list
"${library_root}/callgraph.cc"
@@ -204,10 +194,6 @@ set(sleigh_extra_source_list
"${library_root}/unify.cc"
"${library_root}/xml_arch.cc"
)
# if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
# list(APPEND sleigh_extra_source_list
# )
# endif()
set(sleigh_source_list
"${library_root}/sleigh.cc"
-3
View File
@@ -4,13 +4,11 @@ set(spec_file_list
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/68000/data/languages/68020.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/68000/data/languages/68030.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/68000/data/languages/68040.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/68000/data/languages/CPU32.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/68000/data/languages/coldfire.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8048/data/languages/8048.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/80251.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/80390.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/8051.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/cip-51.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/mx51.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8085/data/languages/8085.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/AARCH64/data/languages/AARCH64.slaspec"
@@ -60,7 +58,6 @@ set(spec_file_list
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/HCS12/data/languages/HC12.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/HCS12/data/languages/HCS12.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/HCS12/data/languages/HCS12X.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Hexagon/data/languages/hexagon.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/JVM/data/languages/JVM.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Loongarch/data/languages/loongarch32_f32.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Loongarch/data/languages/loongarch32_f64.slaspec"
-2
View File
@@ -9,7 +9,6 @@ set(spec_file_list
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/80251.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/80390.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/8051.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/cip-51.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8051/data/languages/mx51.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/8085/data/languages/8085.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/AARCH64/data/languages/AARCH64.slaspec"
@@ -59,7 +58,6 @@ set(spec_file_list
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/HCS12/data/languages/HC12.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/HCS12/data/languages/HCS12.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/HCS12/data/languages/HCS12X.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Hexagon/data/languages/hexagon.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/JVM/data/languages/JVM.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Loongarch/data/languages/loongarch32_f32.slaspec"
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Loongarch/data/languages/loongarch32_f64.slaspec"
+4 -5
View File
@@ -26,11 +26,10 @@ add_executable(sleigh_decomp_test
"${library_root}/../unittests/testparamstore.cc"
)
if(sleigh_RELEASE_IS_HEAD)
target_sources(sleigh_decomp_test PRIVATE
"${library_root}/../unittests/testmultiprec.cc"
)
endif()
# if(sleigh_RELEASE_IS_HEAD)
# target_sources(sleigh_decomp_test PRIVATE
# )
# endif()
target_compile_features(sleigh_decomp_test PRIVATE cxx_std_11)
target_include_directories(sleigh_decomp_test PRIVATE "${library_root}")