mirror of
https://github.com/lifting-bits/sleigh
synced 2026-06-21 13:56:12 +00:00
Compare commits
153 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4fe9a9c46 | |||
| 6f71b8bb38 | |||
| df4e0ff075 | |||
| 5fa7adbd2d | |||
| bfd38a983b | |||
| c83e3ec4d8 | |||
| eeee4453c8 | |||
| d6d9b4c483 | |||
| 7b4f1bce5e | |||
| fa806a45c7 | |||
| c1aec71e40 | |||
| d860b53fa9 | |||
| 532aa25e8a | |||
| 121c65a08b | |||
| 8852ca2b09 | |||
| 8400ecc4ef | |||
| 0bf9bc4334 | |||
| b9c7dcda40 | |||
| 3c525cd665 | |||
| 68e14b713c | |||
| 099ccea772 | |||
| 7887f25eee | |||
| 7d93971e5d | |||
| ddb507d83e | |||
| 1360014bf3 | |||
| 4db93c4191 | |||
| c0160c4205 | |||
| 1d1d978f0d | |||
| 7afb02ef95 | |||
| 6ecd80ee22 | |||
| 0c1eeeae77 | |||
| f696911f67 | |||
| 27289bcc29 | |||
| 876fc733fb | |||
| b6adc0337a | |||
| 4578ac2fba | |||
| 1b2041b6aa | |||
| eb980ff91d | |||
| fbefd7d828 | |||
| 9daaa780c2 | |||
| 0eb9da05b7 | |||
| 76278daf20 | |||
| db0dc4c479 | |||
| efe3b7dcbd | |||
| d4b0316955 | |||
| 86f39ba60f | |||
| 0dd2d21bdc | |||
| d02e760030 | |||
| 42d2ee7457 | |||
| 0664b5c545 | |||
| cd6078bb22 | |||
| fc49d2340e | |||
| 1df5e051d3 | |||
| edc5fabd3a | |||
| 9316045354 | |||
| ec98f45ecf | |||
| e4b6ddb926 | |||
| 9248e9114b | |||
| a020faccb5 | |||
| d74e8e9414 | |||
| f8229f609d | |||
| 4edfcef82e | |||
| 26c6b01b1f | |||
| 563adb57e9 | |||
| 127d913bc9 | |||
| 0ea6de2744 | |||
| 27072f1bca | |||
| 185e34f5e7 | |||
| 1ea24c8b8d | |||
| 572082b008 | |||
| a5496ad829 | |||
| 604cd282a6 | |||
| 14c9c408ca | |||
| 21ed4b78e5 | |||
| d2c42d19ed | |||
| 97f7fd1faf | |||
| cf1dc2bb79 | |||
| a732884632 | |||
| 800a597ff6 | |||
| adcbfb179f | |||
| 64377586f8 | |||
| 19fcb07d42 | |||
| 24e7a85db6 | |||
| 6b8620a8cf | |||
| f398228901 | |||
| d0d9bf5a67 | |||
| 632fa35e36 | |||
| e925db971d | |||
| e590133fc7 | |||
| 760a315270 | |||
| bf13fdebba | |||
| a49bbcc9cf | |||
| 1f41ff02e6 | |||
| 060f42d28c | |||
| eadedecf28 | |||
| 7807d6619a | |||
| 443cfcfdc7 | |||
| d888cdd5ea | |||
| 7a92039e1a | |||
| 4af948df00 | |||
| 95b848e8ed | |||
| 8e27d6ef0a | |||
| 9cc6adc272 | |||
| b763518f2f | |||
| a0a8aa27c0 | |||
| 3efc7415f8 | |||
| 45dc2a0dbb | |||
| b9eef57dee | |||
| f9c852bc57 | |||
| 3e96d296e0 | |||
| 9d4d450551 | |||
| 184fabc5aa | |||
| 4cedffc104 | |||
| 873108dd0e | |||
| 4ddd7f2f89 | |||
| 00f42b7ef7 | |||
| 31868c9d70 | |||
| a96b7fdcc6 | |||
| 78ed150957 | |||
| bc2f60c4b8 | |||
| 85a209708f | |||
| 6f193278c1 | |||
| df49d10291 | |||
| d2f443e358 | |||
| b871c4a6b8 | |||
| 58f4812a02 | |||
| 9a983d5a01 | |||
| fbec9dee2b | |||
| aeea2f6d2e | |||
| e83ec16d28 | |||
| 52c4f0db49 | |||
| ab92d51452 | |||
| 5de8b6c8d6 | |||
| 91ab01ad36 | |||
| db074738f1 | |||
| ca9534df0c | |||
| 2e03227e87 | |||
| bcb4ff6f4a | |||
| ac55146b2c | |||
| 35fbbf873e | |||
| 014530d317 | |||
| 4d8bdb54a3 | |||
| 364b5e4ff9 | |||
| 4519d754ab | |||
| 6db8a80488 | |||
| 39f087356c | |||
| 0c5929b716 | |||
| 84a582a33c | |||
| d8ce6443bf | |||
| ae03ceb39c | |||
| 39d6587cf9 | |||
| 2bb6cfe47b | |||
| c3a84c541a |
@@ -0,0 +1,214 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,72 @@
|
||||
# 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
|
||||
@@ -0,0 +1,153 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,149 @@
|
||||
# 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
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,4 @@
|
||||
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."
|
||||
@@ -0,0 +1,78 @@
|
||||
# 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.
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../.agents/skills
|
||||
@@ -0,0 +1,37 @@
|
||||
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/*
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"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",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
name: Documentation
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch: # manual trigger
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Git User for Applying Patches
|
||||
# See this thread for more details https://github.community/t/github-actions-bot-email-address/17204/5
|
||||
run: |
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y doxygen graphviz
|
||||
|
||||
# Build stable documentation
|
||||
- name: Configure (stable)
|
||||
run: cmake --preset=ci-ubuntu -Dsleigh_RELEASE_TYPE=stable
|
||||
|
||||
- name: Build documentation (stable)
|
||||
run: cmake --build build --target docs
|
||||
|
||||
- name: Copy stable docs
|
||||
run: |
|
||||
mkdir -p docs-site/stable
|
||||
cp -r build/docs/html/* docs-site/stable/
|
||||
|
||||
# Clean and build HEAD documentation
|
||||
- name: Clean build directory
|
||||
run: rm -rf build
|
||||
|
||||
- name: Configure (HEAD)
|
||||
run: cmake --preset=ci-ubuntu -Dsleigh_RELEASE_TYPE=HEAD
|
||||
|
||||
- name: Build documentation (HEAD)
|
||||
run: cmake --build build --target docs
|
||||
|
||||
- name: Copy HEAD docs
|
||||
run: |
|
||||
mkdir -p docs-site/HEAD
|
||||
cp -r build/docs/html/* docs-site/HEAD/
|
||||
|
||||
# Create landing page
|
||||
- name: Create landing page
|
||||
run: |
|
||||
cat > docs-site/index.html << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sleigh Documentation</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #f5f5f5;
|
||||
--text-color: #333;
|
||||
--text-muted: #666;
|
||||
--card-bg: white;
|
||||
--card-shadow: rgba(0,0,0,0.1);
|
||||
--card-shadow-hover: rgba(0,0,0,0.15);
|
||||
--link-color: #0066cc;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg-color: #1a1a1a;
|
||||
--text-color: #e0e0e0;
|
||||
--text-muted: #a0a0a0;
|
||||
--card-bg: #2d2d2d;
|
||||
--card-shadow: rgba(0,0,0,0.3);
|
||||
--card-shadow-hover: rgba(0,0,0,0.4);
|
||||
--link-color: #4da6ff;
|
||||
}
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
background: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
h1 {
|
||||
color: var(--text-color);
|
||||
}
|
||||
.cards {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.card {
|
||||
flex: 1;
|
||||
background: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
box-shadow: 0 2px 4px var(--card-shadow);
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
.card:hover {
|
||||
box-shadow: 0 4px 8px var(--card-shadow-hover);
|
||||
}
|
||||
.card h2 {
|
||||
margin-top: 0;
|
||||
color: var(--link-color);
|
||||
}
|
||||
.card p {
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Sleigh Documentation</h1>
|
||||
<p>Select a documentation version:</p>
|
||||
<div class="cards">
|
||||
<a href="stable/" class="card">
|
||||
<h2>Stable</h2>
|
||||
<p>Documentation for the latest stable release.</p>
|
||||
</a>
|
||||
<a href="HEAD/" class="card">
|
||||
<h2>HEAD</h2>
|
||||
<p>Documentation for the latest Ghidra HEAD development version.</p>
|
||||
</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
- name: Upload Pages artifact
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: docs-site
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
+221
-24
@@ -13,6 +13,9 @@ on:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
LLVM_VERSION: 21
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -20,12 +23,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-13, windows-latest]
|
||||
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@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Git User for Applying Patches
|
||||
# See this thread for more details https://github.community/t/github-actions-bot-email-address/17204/5
|
||||
@@ -43,24 +51,20 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> ${GITHUB_ENV}
|
||||
# Install Python separately to workround recurring homebrew CI issue.
|
||||
# See https://github.com/actions/runner-images/issues/9471 for more details.
|
||||
brew install python@3 || true
|
||||
brew link --overwrite python@3
|
||||
brew install \
|
||||
brew install --formula \
|
||||
ccache \
|
||||
cmake \
|
||||
doxygen \
|
||||
graphviz
|
||||
|
||||
- name: Install Windows system dependencies
|
||||
if: runner.os == 'Windows'
|
||||
uses: nick-fields/retry@v3
|
||||
uses: nick-fields/retry@v4
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: |
|
||||
choco install ccache doxygen.install graphviz
|
||||
Add-Content -Path $env:GITHUB_ENV -Value "CCACHE_EXE=$(Resolve-Path C:\ProgramData\chocolatey\lib\ccache\tools\*\ccache.exe)"
|
||||
vcpkg install zlib:x64-windows-static
|
||||
|
||||
- name: Generate cache key
|
||||
@@ -71,7 +75,7 @@ jobs:
|
||||
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_TIMESTAMP=${current_date}\n")
|
||||
|
||||
- name: Update the cache (ccache)
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: "${{ github.workspace }}/ccache"
|
||||
key: ${{ env.CACHE_KEY }}_ccache_${{ env.CACHE_TIMESTAMP }}
|
||||
@@ -105,12 +109,14 @@ jobs:
|
||||
run: cmake "--preset=ci-$("${{ matrix.os }}".split("-")[0])"
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
-Dsleigh_RELEASE_TYPE=${{ matrix.release }}
|
||||
"-DCMAKE_PROJECT_INCLUDE:FILEPATH=${{ github.workspace }}/cmake/ccache-msvc.cmake"
|
||||
-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded
|
||||
|
||||
- name: Build the project
|
||||
run: cmake
|
||||
--build build
|
||||
--config ${{ matrix.build_type }}
|
||||
-j 2
|
||||
-j 4
|
||||
-v
|
||||
|
||||
- name: Test the project
|
||||
@@ -127,7 +133,7 @@ jobs:
|
||||
- name: Run the example
|
||||
run: cmake
|
||||
--build build
|
||||
-j 2
|
||||
-j 4
|
||||
--config ${{ matrix.build_type }}
|
||||
--target sleigh_example_runner
|
||||
|
||||
@@ -147,7 +153,7 @@ jobs:
|
||||
working-directory: tests/find_package
|
||||
run: |
|
||||
cmake -B build -S . "-Dsleigh_DIR=${{ github.workspace }}/install/lib/cmake/sleigh" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build -j 2 --config ${{ matrix.build_type }}
|
||||
cmake --build build -j 4 --config ${{ matrix.build_type }}
|
||||
cd build
|
||||
ctest -V -C ${{ matrix.build_type }}
|
||||
- name: Test install directory Windows
|
||||
@@ -155,7 +161,7 @@ jobs:
|
||||
working-directory: tests/find_package
|
||||
run: |
|
||||
cmake -B build -S . "-Dsleigh_DIR=${{ github.workspace }}/install/lib/cmake/sleigh" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} "-DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||
cmake --build build -j 2 --config ${{ matrix.build_type }}
|
||||
cmake --build build -j 4 --config ${{ matrix.build_type }}
|
||||
cd build
|
||||
ctest -V -C ${{ matrix.build_type }}
|
||||
|
||||
@@ -164,7 +170,7 @@ jobs:
|
||||
working-directory: extra-tools/sleigh-lift
|
||||
run: |
|
||||
cmake -B build -S . "-Dsleigh_DIR=${{ github.workspace }}/install/lib/cmake/sleigh" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build -j 2 --config ${{ matrix.build_type }}
|
||||
cmake --build build -j 4 --config ${{ matrix.build_type }}
|
||||
cmake --install build --config ${{ matrix.build_type }} --prefix install
|
||||
./install/bin/sleigh-lift --version
|
||||
./install/bin/sleigh-lift disassemble x86-64.sla 4881ecc00f0000
|
||||
@@ -174,7 +180,7 @@ jobs:
|
||||
working-directory: extra-tools/sleigh-lift
|
||||
run: |
|
||||
cmake -B build -S . "-Dsleigh_DIR=${{ github.workspace }}/install/lib/cmake/sleigh" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} "-DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||
cmake --build build -j 2 --config ${{ matrix.build_type }}
|
||||
cmake --build build -j 4 --config ${{ matrix.build_type }}
|
||||
cmake --install build --config ${{ matrix.build_type }} --prefix install
|
||||
./install/bin/sleigh-lift --version
|
||||
./install/bin/sleigh-lift disassemble x86-64.sla 4881ecc00f0000
|
||||
@@ -183,7 +189,7 @@ jobs:
|
||||
- name: Create the packages
|
||||
run: cmake
|
||||
--build build
|
||||
-j 2
|
||||
-j 4
|
||||
--config ${{ matrix.build_type }}
|
||||
--target package
|
||||
|
||||
@@ -193,7 +199,7 @@ jobs:
|
||||
sudo dpkg -i build/*.deb
|
||||
|
||||
cmake -S tests/find_package -B find_package_build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build find_package_build -j 2 --config ${{ matrix.build_type }} --verbose
|
||||
cmake --build find_package_build -j 4 --config ${{ matrix.build_type }} --verbose
|
||||
|
||||
- name: Locate the packages (RelWithDebInfo only)
|
||||
if: matrix.build_type == 'RelWithDebInfo'
|
||||
@@ -216,43 +222,234 @@ jobs:
|
||||
# DEB Package
|
||||
- name: Upload the DEB package artifact (RelWithDebInfo only)
|
||||
if: matrix.build_type == 'RelWithDebInfo' && runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
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@v2.0.8
|
||||
uses: softprops/action-gh-release@v3.0.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@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
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@v2.0.8
|
||||
uses: softprops/action-gh-release@v3.0.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@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
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@v2.0.8
|
||||
uses: softprops/action-gh-release@v3.0.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
|
||||
|
||||
@@ -12,12 +12,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
# Use oldest supported version for maximum script compatibility
|
||||
python-version: '3.7'
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Run Update Script
|
||||
id: head_update
|
||||
@@ -26,29 +26,90 @@ 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/28fa4848947e0faa7fa50647691d01477589d5e9/docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens
|
||||
- uses: tibdex/github-app-token@v2
|
||||
# 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
|
||||
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: Create PR
|
||||
- name: Commit, push, and create PR
|
||||
if: steps.head_update.outputs.did_update
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
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 }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
run: |
|
||||
BRANCH="cron/update-ghidra-${{ steps.head_update.outputs.short_sha }}"
|
||||
|
||||
Changed files:
|
||||
# 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
|
||||
|
||||
${{ steps.head_update.outputs.changed_files }}
|
||||
body: |
|
||||
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 }}
|
||||
branch: cron/update-ghidra-${{ steps.head_update.outputs.short_sha }}
|
||||
delete-branch: true
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
# 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 }}
|
||||
|
||||
Changed files:
|
||||
|
||||
${{ steps.head_update.outputs.changed_files }}
|
||||
|
||||
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"
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# 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)
|
||||
+10
-4
@@ -8,6 +8,9 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
# For MSVC ccache to use /Z7 with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded
|
||||
cmake_policy(SET CMP0141 NEW)
|
||||
|
||||
include(cmake/prelude.cmake)
|
||||
|
||||
# Sets "library_root" variable for decompiler cpp root directory
|
||||
@@ -143,11 +146,14 @@ set(public_include_header_list
|
||||
"${library_root}/multiprecision.hh"
|
||||
"${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)
|
||||
|
||||
+12
-1
@@ -77,7 +77,7 @@
|
||||
{
|
||||
"name": "ci-win64",
|
||||
"inherits": ["flags-windows", "ci-std"],
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"generator": "Visual Studio 18 2026",
|
||||
"architecture": "x64",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
@@ -114,6 +114,17 @@
|
||||
"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,6 +1,6 @@
|
||||
# Sleigh Library
|
||||
|
||||
[Sleigh](https://ghidra.re/courses/languages/html/sleigh.html) is a language used to describe the semantics of instruction sets of general-purpose microprocessors, with enough detail to facilitate the reverse engineering of software compiled for these architectures. It is part of the [Ghidra reverse engineering platform](https://github.com/NationalSecurityAgency/ghidra) and underpins two of its major components: its disassembly and decompilation engines.
|
||||
[Sleigh](https://ghidra.re/ghidra_docs/languages/html/sleigh.html) is a language used to describe the semantics of instruction sets of general-purpose microprocessors, with enough detail to facilitate the reverse engineering of software compiled for these architectures. It is part of the [Ghidra reverse engineering platform](https://github.com/NationalSecurityAgency/ghidra) and underpins two of its major components: its disassembly and decompilation engines.
|
||||
|
||||
This repository provides a CMake-based build project for Sleigh so that it can be built and packaged as a standalone library and be reused in projects other than Ghidra.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# This module configures ccache to work with MSVC
|
||||
# Based on: https://github.com/ccache/ccache/wiki/MS-Visual-Studio
|
||||
|
||||
# Only do this for Windows MSVC builds
|
||||
if(NOT WIN32 OR NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Assume the parent environment has this set
|
||||
# Chocolatey creates a shim which doesn't work when renamed
|
||||
set(CCACHE_EXE "$ENV{CCACHE_EXE}")
|
||||
if(NOT CCACHE_EXE)
|
||||
message(STATUS "ccache not found - MSVC ccache support disabled")
|
||||
return()
|
||||
endif()
|
||||
message(STATUS "Found ccache - ${CCACHE_EXE}")
|
||||
|
||||
message(STATUS "Configuring ccache for MSVC")
|
||||
|
||||
file(COPY_FILE
|
||||
"${CCACHE_EXE}" "${CMAKE_BINARY_DIR}/cl.exe"
|
||||
ONLY_IF_DIFFERENT)
|
||||
|
||||
# By default Visual Studio generators will use /Zi which is not compatible
|
||||
# with ccache, so tell Visual Studio to use /Z7 instead.
|
||||
message(STATUS "Setting MSVC debug information format to 'Embedded'")
|
||||
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
|
||||
|
||||
set(CMAKE_VS_GLOBALS
|
||||
"CLToolExe=cl.exe"
|
||||
"CLToolPath=${CMAKE_BINARY_DIR}"
|
||||
"UseMultiToolTask=true"
|
||||
"DebugInformationFormat=OldStyle"
|
||||
)
|
||||
@@ -123,8 +123,18 @@ static void PrintAssembly(ghidra::Sleigh &engine, uint64_t addr, size_t len) {
|
||||
ghidra::Address cur_addr(engine.getDefaultCodeSpace(), addr),
|
||||
last_addr(engine.getDefaultCodeSpace(), addr + len);
|
||||
while (cur_addr < last_addr) {
|
||||
int32_t instr_len = engine.printAssembly(asm_emit, cur_addr);
|
||||
cur_addr = cur_addr + instr_len;
|
||||
try {
|
||||
int32_t instr_len = engine.printAssembly(asm_emit, cur_addr);
|
||||
cur_addr = cur_addr + instr_len;
|
||||
}
|
||||
catch(ghidra::UnimplError &err) {
|
||||
std::cerr << "UnimplError @ " << cur_addr << " (addr 0x" << addr << ", len 0x" << len << "): " << err.explain << "\n";
|
||||
break;
|
||||
}
|
||||
catch (ghidra::BadDataError &err) {
|
||||
std::cerr << "BadDataError @ " << cur_addr << " (addr 0x" << addr << ", len 0x" << len << "): " << err.explain << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,8 +167,18 @@ static void PrintPcode(ghidra::Sleigh &engine, uint64_t addr, size_t len) {
|
||||
ghidra::Address cur_addr(engine.getDefaultCodeSpace(), addr),
|
||||
last_addr(engine.getDefaultCodeSpace(), addr + len);
|
||||
while (cur_addr < last_addr) {
|
||||
int32_t instr_len = engine.oneInstruction(pcode_emit, cur_addr);
|
||||
cur_addr = cur_addr + instr_len;
|
||||
try {
|
||||
int32_t instr_len = engine.oneInstruction(pcode_emit, cur_addr);
|
||||
cur_addr = cur_addr + instr_len;
|
||||
}
|
||||
catch(ghidra::UnimplError &err) {
|
||||
std::cerr << "UnimplError @ " << cur_addr << " (addr 0x" << addr << ", len 0x" << len << "): " << err.explain << "\n";
|
||||
break;
|
||||
}
|
||||
catch(ghidra::BadDataError &err) {
|
||||
std::cerr << "BadDataError @ " << cur_addr << " (addr 0x" << addr << ", len 0x" << len << "): " << err.explain << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+648
-219
@@ -1,280 +1,709 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script to update CMake files for latest Ghidra Sleigh changes"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import AnyStr, Union, List
|
||||
from typing import List, Dict, Optional, Any, Tuple
|
||||
|
||||
|
||||
# Constants
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
|
||||
HEAD_SPEC_FILE = PROJECT_ROOT / "src" / "spec_files_HEAD.cmake"
|
||||
assert HEAD_SPEC_FILE.exists()
|
||||
SETUP_GHIDRA_FILE = PROJECT_ROOT / "src" / "setup-ghidra-source.cmake"
|
||||
assert SETUP_GHIDRA_FILE.exists()
|
||||
|
||||
# Paths in Ghidra repo that affect this repo. Used with git diff
|
||||
SLEIGH_PATHS: List[str] = [
|
||||
# Source code and tests
|
||||
"Ghidra/Features/Decompiler/src/decompile",
|
||||
# Sleigh files
|
||||
"Ghidra/Processors",
|
||||
# Paths in Ghidra repo that affect this repo
|
||||
SLEIGH_PATHS = [
|
||||
"Ghidra/Features/Decompiler/src/decompile", # Source code and tests
|
||||
"Ghidra/Processors", # Sleigh files
|
||||
]
|
||||
|
||||
GIT_EXE = shutil.which("git")
|
||||
assert GIT_EXE is not None
|
||||
# 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]+)*)"
|
||||
|
||||
|
||||
def msg(s: str) -> None:
|
||||
print(f"[!] {s}")
|
||||
@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()
|
||||
|
||||
|
||||
PathString = Union[AnyStr, Path]
|
||||
class GitHelper:
|
||||
"""Helper class for Git operations"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.git_exe = shutil.which("git")
|
||||
if self.git_exe is None:
|
||||
raise RuntimeError("Git executable not found in PATH")
|
||||
|
||||
def clone_ghidra_git(clone_dir: PathString) -> None:
|
||||
"""Clone the Ghidra git dir at specified directory"""
|
||||
assert GIT_EXE is not None
|
||||
subprocess.run(
|
||||
[
|
||||
GIT_EXE,
|
||||
"clone",
|
||||
"https://github.com/NationalSecurityAgency/ghidra",
|
||||
clone_dir,
|
||||
],
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
check=True,
|
||||
)
|
||||
def run(
|
||||
self, args: List[str], cwd: Path, capture_output: bool = False
|
||||
) -> subprocess.CompletedProcess:
|
||||
"""Run a git command with the given arguments"""
|
||||
assert self.git_exe is not None
|
||||
cmd = [self.git_exe] + args
|
||||
return subprocess.run(
|
||||
cmd,
|
||||
cwd=cwd,
|
||||
stdout=subprocess.PIPE if capture_output else sys.stdout,
|
||||
stderr=subprocess.PIPE if capture_output else sys.stderr,
|
||||
check=True,
|
||||
text=True if capture_output else False,
|
||||
)
|
||||
|
||||
def clone(self, repo_url: str, target_dir: Path) -> None:
|
||||
"""Clone a git repository"""
|
||||
print(f"Cloning {repo_url} to {target_dir}...")
|
||||
self.run(["clone", repo_url, str(target_dir)], cwd=PROJECT_ROOT)
|
||||
|
||||
def git_get_changed_files(
|
||||
repo: Path, old_commit: str, new_commit: str, paths: List[str], ci: bool
|
||||
) -> List[str]:
|
||||
"""Get list of changed files from old_commit to new_commit at the specified paths"""
|
||||
assert GIT_EXE is not None
|
||||
changed_files = (
|
||||
subprocess.run(
|
||||
def get_head_commit(self, repo_dir: Path) -> str:
|
||||
"""Get the HEAD commit SHA of the repository"""
|
||||
result = self.run(["rev-parse", "HEAD"], cwd=repo_dir, capture_output=True)
|
||||
return result.stdout.strip()
|
||||
|
||||
def check_commit_exists(self, repo_dir: Path, commit: str) -> bool:
|
||||
"""Check if a commit exists in the repository"""
|
||||
try:
|
||||
self.run(["cat-file", "-e", commit], repo_dir, capture_output=True)
|
||||
return True
|
||||
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]]:
|
||||
"""Get detailed information about commits affecting specified paths"""
|
||||
result = self.run(
|
||||
[
|
||||
"log",
|
||||
"--pretty=format:%H%n%ad%n%s%n%b%n====",
|
||||
"--date=iso",
|
||||
f"{old_commit}..{new_commit}",
|
||||
"--",
|
||||
*paths,
|
||||
],
|
||||
cwd=repo_dir,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
log_output = result.stdout.strip()
|
||||
commits = []
|
||||
|
||||
if log_output:
|
||||
commit_sections = log_output.split("\n====")
|
||||
|
||||
for section in commit_sections:
|
||||
if not section.strip():
|
||||
continue
|
||||
|
||||
lines = section.strip().split("\n")
|
||||
commit_hash = lines[0]
|
||||
commit_date = lines[1]
|
||||
commit_msg = lines[2]
|
||||
body = "\n".join(lines[3:]) if len(lines) > 3 else ""
|
||||
|
||||
# Get files modified in this commit
|
||||
files_result = self.run(
|
||||
[
|
||||
"diff-tree",
|
||||
"--no-commit-id",
|
||||
"--name-status",
|
||||
"-r",
|
||||
commit_hash,
|
||||
"--",
|
||||
*paths,
|
||||
],
|
||||
cwd=repo_dir,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
if commit_files:
|
||||
commits.append(
|
||||
{
|
||||
"hash": commit_hash,
|
||||
"date": commit_date,
|
||||
"message": commit_msg,
|
||||
"body": body,
|
||||
"files": commit_files,
|
||||
}
|
||||
)
|
||||
|
||||
return commits
|
||||
|
||||
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)
|
||||
"""
|
||||
result = self.run(
|
||||
[
|
||||
GIT_EXE,
|
||||
"diff",
|
||||
"--name-status",
|
||||
f"{old_commit}...{new_commit}",
|
||||
"--",
|
||||
*paths,
|
||||
],
|
||||
cwd=repo,
|
||||
cwd=repo_dir,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
.stdout.decode()
|
||||
.strip()
|
||||
.splitlines()
|
||||
)
|
||||
num_changed = len(changed_files)
|
||||
if num_changed > 0:
|
||||
msg(f"Found {num_changed} changed sleigh files:")
|
||||
print("\n".join(changed_files))
|
||||
if ci:
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as gh_out:
|
||||
gh_out.write("changed_files<<EOF\n")
|
||||
gh_out.write("```\n")
|
||||
gh_out.write("\n".join(changed_files))
|
||||
gh_out.write("\n```\n")
|
||||
gh_out.write("EOF\n")
|
||||
return changed_files
|
||||
|
||||
raw_lines = result.stdout.strip().splitlines()
|
||||
filtered_files = []
|
||||
categorized = CategorizedChanges()
|
||||
|
||||
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
|
||||
|
||||
|
||||
def is_sleigh_updated(
|
||||
ghidra_repo: Path, old_commit: str, new_commit: str, ci: bool
|
||||
) -> bool:
|
||||
"""Check if files we're interested in have been touched at all"""
|
||||
changed_files = git_get_changed_files(
|
||||
ghidra_repo, old_commit, new_commit, SLEIGH_PATHS, ci
|
||||
)
|
||||
return len(changed_files) > 0
|
||||
class GhidraUpdater:
|
||||
"""Handles updating Ghidra-related CMake files"""
|
||||
|
||||
def __init__(self, ci_mode: bool = False, dry_run: bool = False) -> None:
|
||||
self.git = GitHelper()
|
||||
self.ci_mode = ci_mode
|
||||
self.dry_run = dry_run
|
||||
|
||||
def update_head_commit(
|
||||
setup_file: Path, ghidra_repo_dir: PathString, latest_commit: str, ci: bool
|
||||
) -> bool:
|
||||
"""Edit the Ghidra script to point to the latest commit"""
|
||||
head_commit_line = r"set\(ghidra_head_git_tag \"([0-9A-Fa-f]+)\"\)"
|
||||
updated = False
|
||||
# Validate required paths
|
||||
if not HEAD_SPEC_FILE.exists():
|
||||
raise FileNotFoundError(f"HEAD spec file not found: {HEAD_SPEC_FILE}")
|
||||
if not SETUP_GHIDRA_FILE.exists():
|
||||
raise FileNotFoundError(f"Setup Ghidra file not found: {SETUP_GHIDRA_FILE}")
|
||||
|
||||
fd, abspath = tempfile.mkstemp()
|
||||
with open(fd, "w") as w:
|
||||
with setup_file.open("r") as r:
|
||||
for line in r:
|
||||
match = re.search(head_commit_line, line)
|
||||
if match is not None:
|
||||
# Set up GitHub Actions outputs if in CI mode
|
||||
if self.ci_mode and "GITHUB_OUTPUT" not in os.environ:
|
||||
raise RuntimeError("CI mode requires GITHUB_OUTPUT environment variable")
|
||||
|
||||
def clone_ghidra_if_needed(
|
||||
self, repo_dir: Optional[Path] = None
|
||||
) -> Tuple[Path, Optional[tempfile.TemporaryDirectory]]:
|
||||
"""Clone Ghidra repo if a directory is not provided"""
|
||||
temp_dir = None
|
||||
|
||||
if repo_dir is None:
|
||||
temp_dir = tempfile.TemporaryDirectory()
|
||||
repo_dir = Path(temp_dir.name) / "ghidra"
|
||||
self.git.clone("https://github.com/NationalSecurityAgency/ghidra", repo_dir)
|
||||
|
||||
return repo_dir, temp_dir
|
||||
|
||||
def log_github_output(self, key: str, value: str) -> None:
|
||||
"""Log output for GitHub Actions"""
|
||||
if self.ci_mode:
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
||||
f.write(f"{key}={value}\n")
|
||||
|
||||
def log_github_multiline_output(self, key: str, value: str) -> None:
|
||||
"""Log multiline output for GitHub Actions"""
|
||||
if self.ci_mode:
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
||||
f.write(f"{key}<<EOF\n")
|
||||
f.write(value)
|
||||
f.write("\nEOF\n")
|
||||
|
||||
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(
|
||||
repo_dir, start_commit, end_commit, SLEIGH_PATHS
|
||||
)
|
||||
|
||||
if not changed_files:
|
||||
print("No sleigh files were modified between these commits")
|
||||
return [], [], CategorizedChanges()
|
||||
|
||||
# Output changes for logging
|
||||
num_changed = len(changed_files)
|
||||
print(f"Found {num_changed} changed sleigh files:")
|
||||
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
|
||||
)
|
||||
|
||||
if commit_info:
|
||||
print(f"\nCommits affecting sleigh files ({len(commit_info)}):\n")
|
||||
for i, commit in enumerate(commit_info, 1):
|
||||
print(f"[Commit {i}/{len(commit_info)}]")
|
||||
print(f"Hash: {commit['hash']}")
|
||||
print(f"Date: {commit['date']}")
|
||||
print(f"Message: {commit['message']}")
|
||||
if commit["body"]:
|
||||
print(f"Details:\n{commit['body']}")
|
||||
print("\nFiles changed:")
|
||||
for file in commit["files"]:
|
||||
print(f" {file}")
|
||||
print("")
|
||||
|
||||
# Log outputs for GitHub Actions
|
||||
if self.ci_mode:
|
||||
self.log_github_output("short_sha", end_commit[:9])
|
||||
self.log_github_output("did_update", "true")
|
||||
|
||||
# Log changed files
|
||||
changed_files_str = "```\n" + "\n".join(changed_files) + "\n```"
|
||||
self.log_github_multiline_output("changed_files", changed_files_str)
|
||||
|
||||
# Log commit details
|
||||
if commit_info:
|
||||
details = ["```"]
|
||||
for i, commit in enumerate(commit_info, 1):
|
||||
details.append(f"[Commit {i}/{len(commit_info)}]")
|
||||
details.append(f"Hash: {commit['hash']}")
|
||||
details.append(f"Date: {commit['date']}")
|
||||
details.append(f"Message: {commit['message']}")
|
||||
if commit["body"]:
|
||||
details.append(f"Details:\n{commit['body']}")
|
||||
details.append("\nFiles changed:")
|
||||
for file in commit["files"]:
|
||||
details.append(f" {file}")
|
||||
details.append("")
|
||||
# Replace trailing newline for last entry
|
||||
details[-1] = "```"
|
||||
|
||||
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
|
||||
|
||||
def update_head_commit(
|
||||
self, repo_dir: Path, setup_file: Path
|
||||
) -> Tuple[bool, str, str]:
|
||||
"""Update the HEAD commit in the setup file if needed"""
|
||||
# Get latest commit hash
|
||||
latest_commit = self.git.get_head_commit(repo_dir)
|
||||
current_commit = None
|
||||
|
||||
# Find current commit hash in setup file
|
||||
with setup_file.open("r") as f:
|
||||
for line in f:
|
||||
match = re.search(HEAD_COMMIT_PATTERN, line)
|
||||
if match:
|
||||
current_commit = match.group(1)
|
||||
if current_commit != latest_commit:
|
||||
msg(f"Found new commit: {latest_commit}")
|
||||
if is_sleigh_updated(
|
||||
ghidra_repo_dir, current_commit, latest_commit, ci
|
||||
):
|
||||
line = re.sub(
|
||||
head_commit_line,
|
||||
f'set(ghidra_head_git_tag "{latest_commit}")',
|
||||
line,
|
||||
)
|
||||
updated = True
|
||||
else:
|
||||
msg("No sleigh files updated")
|
||||
w.write(line)
|
||||
break
|
||||
|
||||
# Make the swap with the new content
|
||||
shutil.copymode(setup_file, abspath)
|
||||
os.remove(setup_file)
|
||||
shutil.move(abspath, setup_file)
|
||||
return updated
|
||||
if current_commit is None:
|
||||
raise ValueError("Could not find current commit in setup file")
|
||||
|
||||
# Check if update is needed
|
||||
if current_commit == latest_commit:
|
||||
print(f"Already at the latest commit: {latest_commit}")
|
||||
return False, current_commit, latest_commit
|
||||
|
||||
def update_head_version_file(setup_file: Path, ghidra_root_dir: PathString) -> None:
|
||||
"""Edit the Ghidra script to point to the latest version"""
|
||||
cmake_head_version_line = (
|
||||
r"set\(ghidra_head_version \"([0-9]+(\.[0-9]+)?(\.[0-9]+)?)\"\)"
|
||||
)
|
||||
print(f"Found new commit: {latest_commit}")
|
||||
|
||||
with (ghidra_root_dir / "Ghidra" / "application.properties").open("r") as f:
|
||||
content = f.read()
|
||||
match = re.search(
|
||||
r"application.version=([0-9]+(\.[0-9]+)?(\.[0-9]+)?)", content
|
||||
# Check if sleigh files were updated and display changes
|
||||
changed_files, commit_info, _ = self.display_changes(
|
||||
repo_dir, current_commit, latest_commit
|
||||
)
|
||||
assert match is not None
|
||||
source_version = match.group(1)
|
||||
|
||||
with setup_file.open("r") as f:
|
||||
content = f.read()
|
||||
match = re.search(cmake_head_version_line, content)
|
||||
assert match is not None
|
||||
cmake_version = match.group(1)
|
||||
if not changed_files:
|
||||
return False, current_commit, latest_commit
|
||||
|
||||
if cmake_version == source_version:
|
||||
msg(f"No new version bump")
|
||||
return
|
||||
# Update the setup file if not in dry run mode
|
||||
if not self.dry_run:
|
||||
self._replace_in_file(
|
||||
setup_file,
|
||||
HEAD_COMMIT_PATTERN,
|
||||
f'set(ghidra_head_git_tag "{latest_commit}")',
|
||||
)
|
||||
|
||||
msg(f"Found new version: {source_version}")
|
||||
fd, abspath = tempfile.mkstemp()
|
||||
with open(fd, "w") as w:
|
||||
with setup_file.open("r") as r:
|
||||
for line in r:
|
||||
match = re.search(cmake_head_version_line, line)
|
||||
if match is not None:
|
||||
line = re.sub(
|
||||
cmake_head_version_line,
|
||||
f'set(ghidra_head_version "{source_version}")',
|
||||
line,
|
||||
)
|
||||
w.write(line)
|
||||
return True, current_commit, latest_commit
|
||||
|
||||
# Make the swap with the new content
|
||||
shutil.copymode(setup_file, abspath)
|
||||
os.remove(setup_file)
|
||||
shutil.move(abspath, setup_file)
|
||||
def update_version(self, repo_dir: Path, setup_file: Path) -> None:
|
||||
"""Update the Ghidra version in the setup file if needed"""
|
||||
# Get source version from application.properties
|
||||
app_properties_file = repo_dir / "Ghidra" / "application.properties"
|
||||
|
||||
with app_properties_file.open("r") as f:
|
||||
content = f.read()
|
||||
match = re.search(APP_VERSION_PATTERN, content)
|
||||
if not match:
|
||||
raise ValueError("Could not find version in application.properties")
|
||||
source_version = match.group(1)
|
||||
|
||||
# Get current version from setup file
|
||||
with setup_file.open("r") as f:
|
||||
content = f.read()
|
||||
match = re.search(VERSION_PATTERN, content)
|
||||
if not match:
|
||||
raise ValueError("Could not find version in setup file")
|
||||
cmake_version = match.group(1)
|
||||
|
||||
# Check if update is needed
|
||||
if cmake_version == source_version:
|
||||
print("No new version bump")
|
||||
return
|
||||
|
||||
print(f"Found new version: {source_version}")
|
||||
|
||||
# Update the setup file if not in dry run mode
|
||||
if not self.dry_run:
|
||||
self._replace_in_file(
|
||||
setup_file,
|
||||
VERSION_PATTERN,
|
||||
f'set(ghidra_head_version "{source_version}")',
|
||||
)
|
||||
|
||||
def update_spec_files(self, repo_dir: Path, spec_file: Path) -> None:
|
||||
"""Update the list of spec files in the CMake file"""
|
||||
# Find all .slaspec files
|
||||
spec_files = []
|
||||
processors_dir = repo_dir / "Ghidra" / "Processors"
|
||||
|
||||
for path in processors_dir.glob("**/*.slaspec"):
|
||||
spec_files.append(path.relative_to(repo_dir))
|
||||
|
||||
spec_files.sort()
|
||||
print(f"Found {len(spec_files)} slaspec files")
|
||||
|
||||
# Write the updated spec file list
|
||||
if not self.dry_run and spec_files:
|
||||
with spec_file.open("w") as f:
|
||||
f.write("set(spec_file_list\n")
|
||||
for spec in spec_files:
|
||||
f.write(f' "${{ghidrasource_SOURCE_DIR}}/{spec}"\n')
|
||||
f.write(")\n")
|
||||
|
||||
def _replace_in_file(self, file_path: Path, pattern: str, replacement: str) -> None:
|
||||
"""Replace text in a file matching the pattern with the replacement"""
|
||||
temp_file = tempfile.NamedTemporaryFile(mode="w", delete=False)
|
||||
|
||||
with file_path.open("r") as src, open(temp_file.name, "w") as dst:
|
||||
for line in src:
|
||||
dst.write(re.sub(pattern, replacement, line))
|
||||
|
||||
# Replace the original file with the modified one
|
||||
shutil.copymode(file_path, temp_file.name)
|
||||
os.remove(file_path)
|
||||
shutil.move(temp_file.name, file_path)
|
||||
|
||||
def update(self, repo_dir: Optional[Path] = None) -> bool:
|
||||
"""Main update method to orchestrate the update process"""
|
||||
# Clone repo if not provided
|
||||
repo_dir, temp_dir = self.clone_ghidra_if_needed(repo_dir)
|
||||
|
||||
try:
|
||||
# Update the HEAD commit
|
||||
did_update, _, _ = self.update_head_commit(repo_dir, SETUP_GHIDRA_FILE)
|
||||
|
||||
# If commit was updated, also update version and spec files
|
||||
if did_update:
|
||||
self.update_version(repo_dir, SETUP_GHIDRA_FILE)
|
||||
self.update_spec_files(repo_dir, HEAD_SPEC_FILE)
|
||||
|
||||
return did_update
|
||||
|
||||
finally:
|
||||
# Clean up temp directory if created
|
||||
if temp_dir:
|
||||
temp_dir.cleanup()
|
||||
|
||||
def compare_commits(
|
||||
self, repo_dir: Path, start_commit: str, end_commit: Optional[str] = None
|
||||
) -> None:
|
||||
"""Compare changes between two commits without updating any files"""
|
||||
# If end_commit is not provided, use HEAD
|
||||
if end_commit is None:
|
||||
end_commit = self.git.get_head_commit(repo_dir)
|
||||
print(f"Using HEAD as end commit: {end_commit}")
|
||||
|
||||
print(f"Comparing commits {start_commit} to {end_commit}")
|
||||
|
||||
# Check if the commits exist
|
||||
for commit in [start_commit, end_commit]:
|
||||
if not self.git.check_commit_exists(repo_dir, commit):
|
||||
raise ValueError(f"Commit {commit} does not exist in the repository")
|
||||
|
||||
# Display changes
|
||||
self.display_changes(repo_dir, start_commit, end_commit)
|
||||
|
||||
|
||||
def update_spec_files(ghidra_repo_dir: PathString, cmake_file: PathString):
|
||||
"""Based on the files in the Ghidra repo, write an updated list of spec files."""
|
||||
spec_files = []
|
||||
for dirpath, _, fnames in os.walk(ghidra_repo_dir / "Ghidra" / "Processors"):
|
||||
for file in fnames:
|
||||
if file.endswith(".slaspec"):
|
||||
spec_files.append((Path(dirpath) / file).relative_to(ghidra_repo_dir))
|
||||
assert len(spec_files) > 0
|
||||
spec_files.sort()
|
||||
|
||||
msg(f"Found {len(spec_files)} slaspec files")
|
||||
|
||||
with open(cmake_file, "w") as f:
|
||||
f.write("set(spec_file_list\n")
|
||||
for spec in spec_files:
|
||||
f.write(f' "${{ghidrasource_SOURCE_DIR}}/{spec}"\n')
|
||||
f.write(")\n")
|
||||
|
||||
|
||||
def get_latest_commit(ghidra_repo_dir: PathString) -> str:
|
||||
"""Get the commit SHA that the repo is currently at"""
|
||||
assert GIT_EXE is not None
|
||||
return (
|
||||
subprocess.run(
|
||||
[GIT_EXE, "rev-parse", "HEAD"],
|
||||
cwd=ghidra_repo_dir,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
.stdout.decode()
|
||||
.strip()
|
||||
)
|
||||
|
||||
|
||||
def update_head(
|
||||
setup_file: Path, spec_file: Path, ghidra_repo_dir: PathString, ci: bool
|
||||
) -> bool:
|
||||
"""Update to latest head and make changes to the CMake files"""
|
||||
tmpdirname = None
|
||||
if ghidra_repo_dir is None:
|
||||
tmpdirname = tempfile.TemporaryDirectory()
|
||||
ghidra_repo_dir = Path(tmpdirname.name) / "ghidra"
|
||||
clone_ghidra_git(ghidra_repo_dir)
|
||||
|
||||
latest_commit = get_latest_commit(ghidra_repo_dir)
|
||||
did_update_commit = update_head_commit(
|
||||
setup_file, ghidra_repo_dir, latest_commit, ci
|
||||
)
|
||||
if did_update_commit:
|
||||
if ci:
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as gh_out:
|
||||
gh_out.write(f"short_sha={latest_commit[:9]}\n")
|
||||
gh_out.write("did_update=true\n")
|
||||
update_spec_files(ghidra_repo_dir, spec_file)
|
||||
update_head_version_file(setup_file, ghidra_repo_dir)
|
||||
else:
|
||||
msg(f"Already at the latest commit: {latest_commit}")
|
||||
|
||||
if tmpdirname is not None:
|
||||
tmpdirname.cleanup()
|
||||
|
||||
if did_update_commit:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse, os
|
||||
|
||||
def dir_path(string):
|
||||
if string is None:
|
||||
return string
|
||||
string = Path(string).expanduser().resolve()
|
||||
if string.is_dir():
|
||||
return string
|
||||
else:
|
||||
raise NotADirectoryError(string)
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""Parse command line arguments"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Update CMake files to latest Ghidra commit."
|
||||
description="Find and collect changes between two Ghidra commits. Update CMake files to latest Ghidra commit if specified."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--ghidra-repo",
|
||||
type=dir_path,
|
||||
type=str,
|
||||
help="Use a specific Ghidra repo directory instead of downloading it from the internet",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--ci",
|
||||
action="store_true",
|
||||
help="Output GitHub Actions commands for recording information in CI",
|
||||
help="Output GitHub Actions commands for recording information in CI. Requires GITHUB_OUTPUT environment variable.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be changed without actually modifying any files",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"start_commit",
|
||||
nargs="?",
|
||||
type=str,
|
||||
help="Starting commit for comparison. When specified, no CMake files will be updated.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"end_commit",
|
||||
nargs="?",
|
||||
type=str,
|
||||
help="Ending commit for comparison. If not specified, uses current HEAD of the repo. Requires start_commit.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.ci:
|
||||
assert (
|
||||
"GITHUB_OUTPUT" in os.environ
|
||||
), "CI needs `GITHUB_OUTPUT` environment variable set to a file location"
|
||||
# Convert ghidra-repo path if provided
|
||||
if args.ghidra_repo:
|
||||
repo_path = Path(args.ghidra_repo).expanduser().resolve()
|
||||
if not repo_path.is_dir():
|
||||
parser.error(f"Ghidra repo directory does not exist: {repo_path}")
|
||||
args.ghidra_repo = repo_path
|
||||
|
||||
if not update_head(SETUP_GHIDRA_FILE, HEAD_SPEC_FILE, args.ghidra_repo, args.ci):
|
||||
msg("No update required")
|
||||
else:
|
||||
msg("Update required!")
|
||||
# Validate commit arguments
|
||||
if args.end_commit and not args.start_commit:
|
||||
parser.error("Cannot specify end_commit without start_commit")
|
||||
|
||||
# If commits are specified, a Ghidra repo is required
|
||||
if args.start_commit and not args.ghidra_repo:
|
||||
parser.error("--ghidra-repo is required when specifying commits")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main entry point"""
|
||||
args = parse_args()
|
||||
|
||||
try:
|
||||
updater = GhidraUpdater(ci_mode=args.ci, dry_run=args.dry_run)
|
||||
|
||||
# If start_commit is specified, run in comparison mode
|
||||
if args.start_commit:
|
||||
updater.compare_commits(
|
||||
args.ghidra_repo, args.start_commit, args.end_commit
|
||||
)
|
||||
else:
|
||||
# Normal update mode
|
||||
did_update = updater.update(args.ghidra_repo)
|
||||
|
||||
if not did_update:
|
||||
print("No update required")
|
||||
elif args.dry_run:
|
||||
print("Update would be required!")
|
||||
else:
|
||||
print("Update required!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,82 +1,22 @@
|
||||
From fc4ac7901ae08cd018d94ce78027cd6636251d39 Mon Sep 17 00:00:00 2001
|
||||
From 98769d3cd2f417a52559bf68700e2f9f3e7a0e4f 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/5] Fix UBSAN errors in decompiler
|
||||
Subject: [PATCH 1/7] Fix UBSAN errors in decompiler
|
||||
|
||||
Co-authored-by: Alex Cameron <asc@tetsuo.sh>
|
||||
---
|
||||
.../Decompiler/src/decompile/cpp/fspec.cc | 8 ++++++--
|
||||
.../Decompiler/src/decompile/cpp/op.cc | 6 +++++-
|
||||
.../Decompiler/src/decompile/cpp/opbehavior.cc | 8 +++++++-
|
||||
.../src/decompile/cpp/pcodecompile.cc | 18 +++++++++++-------
|
||||
.../Decompiler/src/decompile/cpp/ruleaction.cc | 18 ++++++++++++++----
|
||||
.../Decompiler/src/decompile/cpp/semantics.cc | 2 ++
|
||||
.../Decompiler/src/decompile/cpp/semantics.hh | 2 +-
|
||||
.../src/decompile/cpp/slgh_compile.cc | 2 +-
|
||||
.../Decompiler/src/decompile/cpp/type.cc | 2 +-
|
||||
.../src/decompile/unittests/testfloatemu.cc | 2 +-
|
||||
10 files changed, 49 insertions(+), 19 deletions(-)
|
||||
5 files changed, 16 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc
|
||||
index 6ebec206c9..e5a1ad598c 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc
|
||||
@@ -2868,8 +2868,12 @@ void ProtoModelMerged::decode(Decoder &decoder)
|
||||
modellist.push_back(mymodel);
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
- ((ParamListMerged *)input)->finalize();
|
||||
- ((ParamListMerged *)output)->finalize();
|
||||
+ if (input->getType() == ParamList::p_merged) {
|
||||
+ ((ParamListMerged *)input)->finalize();
|
||||
+ }
|
||||
+ if (output->getType() == ParamList::p_merged) {
|
||||
+ ((ParamListMerged *)output)->finalize();
|
||||
+ }
|
||||
}
|
||||
|
||||
void ParameterBasic::setTypeLock(bool val)
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc
|
||||
index d76a799960..37ba971a34 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc
|
||||
@@ -666,7 +666,11 @@ uintb PcodeOp::getNZMaskLocal(bool cliploop) const
|
||||
break;
|
||||
case CPUI_PIECE:
|
||||
resmask = getIn(0)->getNZMask();
|
||||
- resmask <<= 8*getIn(1)->getSize();
|
||||
+ if (8*getIn(1)->getSize() < sizeof(resmask)) {
|
||||
+ resmask <<= 8*getIn(1)->getSize();
|
||||
+ } else {
|
||||
+ resmask = 0;
|
||||
+ }
|
||||
resmask |= getIn(1)->getNZMask();
|
||||
break;
|
||||
case CPUI_INT_MULT:
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc
|
||||
index aebcfd9103..6c47e6eb15 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc
|
||||
@@ -746,7 +746,13 @@ uintb OpBehaviorPiece::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb i
|
||||
uintb OpBehaviorSubpiece::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const
|
||||
|
||||
{
|
||||
- uintb res = (in1>>(in2*8)) & calc_mask(sizeout);
|
||||
+ uintb res = in1;
|
||||
+ if (in2 < sizeof(in1)) {
|
||||
+ res >>= (in2*8);
|
||||
+ } else {
|
||||
+ res = 0;
|
||||
+ }
|
||||
+ res &= calc_mask(sizeout);
|
||||
return res;
|
||||
}
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
|
||||
index ca9d71ab99..85d4dd281d 100644
|
||||
index a67a3de849..37ba4930e6 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
|
||||
@@ -621,8 +621,10 @@ vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin
|
||||
@@ -618,8 +618,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;
|
||||
@@ -89,7 +29,7 @@ index ca9d71ab99..85d4dd281d 100644
|
||||
|
||||
if (vn->getSize().getType()==ConstTpl::real) {
|
||||
// If we know the size of the bitranged varnode, we can
|
||||
@@ -726,9 +728,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
@@ -723,9 +725,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +39,7 @@ index ca9d71ab99..85d4dd281d 100644
|
||||
if (truncneeded && ((bitoffset % 8)==0)) {
|
||||
truncshift = bitoffset/8;
|
||||
bitoffset = 0;
|
||||
@@ -751,8 +750,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
@@ -748,8 +747,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
appendOp(CPUI_INT_RIGHT,res,bitoffset,4);
|
||||
if (truncneeded)
|
||||
appendOp(CPUI_SUBPIECE,res,truncshift,4);
|
||||
@@ -115,51 +55,8 @@ index ca9d71ab99..85d4dd281d 100644
|
||||
force_size(res->outvn,ConstTpl(ConstTpl::real,finalsize),*res->ops);
|
||||
return res;
|
||||
}
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
|
||||
index f667c6ca95..6ea3916520 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
|
||||
@@ -977,7 +977,12 @@ int4 RulePullsubIndirect::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Varnode *outvn = op->getOut();
|
||||
if (outvn->isPrecisLo()||outvn->isPrecisHi()) return 0; // Don't pull apart double precision object
|
||||
|
||||
- uintb consume = calc_mask(newSize) << 8 * minByte;
|
||||
+ uintb consume = calc_mask(newSize);
|
||||
+ if (8 * minByte < sizeof(consume)) {
|
||||
+ consume <<= 8 * minByte;
|
||||
+ } else {
|
||||
+ consume = 0;
|
||||
+ }
|
||||
consume = ~consume;
|
||||
if ((consume & indir->getIn(0)->getConsume())!=0) return 0;
|
||||
|
||||
@@ -7026,8 +7031,9 @@ int4 RulePtrsubCharConstant::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Varnode *sb = op->getIn(0);
|
||||
Datatype *sbType = sb->getTypeReadFacing(op);
|
||||
if (sbType->getMetatype() != TYPE_PTR) return 0;
|
||||
- TypeSpacebase *sbtype = (TypeSpacebase *)((TypePointer *)sbType)->getPtrTo();
|
||||
- if (sbtype->getMetatype() != TYPE_SPACEBASE) return 0;
|
||||
+ Datatype *sbTypePtr = ((TypePointer *)sbType)->getPtrTo();
|
||||
+ if (sbTypePtr->getMetatype() != TYPE_SPACEBASE) return 0;
|
||||
+ TypeSpacebase *sbtype = (TypeSpacebase *)sbTypePtr;
|
||||
Varnode *vn1 = op->getIn(1);
|
||||
if (!vn1->isConstant()) return 0;
|
||||
Varnode *outvn = op->getOut();
|
||||
@@ -8831,7 +8837,11 @@ int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Varnode *outvn = op->getOut();
|
||||
int4 flowsize = outvn->getSize();
|
||||
uintb mask = calc_mask( flowsize );
|
||||
- mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||
+ if (8*((int4)op->getIn(1)->getOffset()) < sizeof(mask)) {
|
||||
+ mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||
+ } else {
|
||||
+ mask = 0;
|
||||
+ }
|
||||
bool aggressive = outvn->isPtrFlow();
|
||||
if (!aggressive) {
|
||||
if ((vn->getConsume() & mask) != vn->getConsume()) return 0;
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
|
||||
index cd9b9835b1..8a4616c3b9 100644
|
||||
index 18e2ff8ba1..3bfe29f2ef 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)
|
||||
@@ -179,7 +76,7 @@ index cd9b9835b1..8a4616c3b9 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 e0b069959d..9117a45c75 100644
|
||||
index b53b18797d..b2f043e32d 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 {
|
||||
@@ -192,10 +89,10 @@ index e0b069959d..9117a45c75 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 50d85e22ba..9f3b456229 100644
|
||||
index 75bebffcb0..bf5e7ce681 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
|
||||
@@ -2164,8 +2164,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
|
||||
@@ -2323,8 +2323,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
|
||||
ostringstream msg;
|
||||
SymbolTree::const_iterator iter;
|
||||
for(iter=scope->begin();iter!=scope->end();++iter) {
|
||||
@@ -205,20 +102,6 @@ index 50d85e22ba..9f3b456229 100644
|
||||
if (sym->getRefCount() == 0)
|
||||
msg << " Label <" << sym->getName() << "> was placed but not used" << endl;
|
||||
else if (!sym->isPlaced())
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
|
||||
index 7cda43127f..20a5bc1c83 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
|
||||
@@ -3661,8 +3661,8 @@ void TypeFactory::recalcPointerSubmeta(Datatype *base,sub_metatype sub)
|
||||
top.submeta = sub; // Search on the incorrect submeta
|
||||
iter = tree.lower_bound(&top);
|
||||
while(iter != tree.end()) {
|
||||
+ if ((*iter)->getMetatype() != TYPE_PTR) break;
|
||||
TypePointer *ptr = (TypePointer *)*iter;
|
||||
- if (ptr->getMetatype() != TYPE_PTR) break;
|
||||
if (ptr->ptrto != base) break;
|
||||
++iter;
|
||||
if (ptr->submeta == sub) {
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testfloatemu.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testfloatemu.cc
|
||||
index 2571f55f1a..fe40e22b1b 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/unittests/testfloatemu.cc
|
||||
@@ -233,5 +116,5 @@ index 2571f55f1a..fe40e22b1b 100644
|
||||
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
|
||||
uintb encoding = format.getEncoding(f);
|
||||
--
|
||||
2.47.0
|
||||
2.51.1
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 1e334bf169139dd1dcbf8e92c992b702b027b3b1 Mon Sep 17 00:00:00 2001
|
||||
From 4e6f277eae589c4f9bceb40337d43384248d272c 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/5] Use `stroull` instead of `stroul` to parse address
|
||||
Subject: [PATCH 2/7] 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.47.0
|
||||
2.51.1
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
From 832fb17127459f0fe6bb141dd499d94dfd6f4e6f Mon Sep 17 00:00:00 2001
|
||||
From: Eric Kilmer <eric.d.kilmer@gmail.com>
|
||||
Date: Sat, 30 Mar 2024 18:46:16 -0400
|
||||
Subject: [PATCH 3/5] Add missing index check to prevent errors in Windows
|
||||
|
||||
Not sure why this only appears when testing on Windows.
|
||||
---
|
||||
Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
|
||||
index 0237adb820..5048a9d480 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
|
||||
@@ -1844,7 +1844,7 @@ void IfcProtooverride::execute(istream &s)
|
||||
s >> ws;
|
||||
Address callpoint(parse_machaddr(s,discard,*dcp->conf->types));
|
||||
int4 i;
|
||||
- for(i=0;dcp->fd->numCalls();++i)
|
||||
+ for(i=0;i<dcp->fd->numCalls();++i)
|
||||
if (dcp->fd->getCallSpecs(i)->getOp()->getAddr() == callpoint) break;
|
||||
if (i == dcp->fd->numCalls())
|
||||
throw IfaceExecutionError("No call is made at this address");
|
||||
--
|
||||
2.47.0
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
From 163be34210f69068f89e20acf89a4fbd50a88b76 Mon Sep 17 00:00:00 2001
|
||||
From 41eb6be342d29d6ff618d06b391bf33209fe8ade 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 5/5] Ignore floating point test due to compilation differences
|
||||
Subject: [PATCH 3/7] 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.47.0
|
||||
2.51.1
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
From 8aa8bf7a146ab43f5b1b27386667af52269720fd 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
|
||||
point test
|
||||
|
||||
At least on Apple Silicon, this test reports positive NAN.
|
||||
|
||||
Use the regex optional operator '?' to allow for positive or negative
|
||||
NAN.
|
||||
---
|
||||
.../Decompiler/src/decompile/datatests/floatprint.xml | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml
|
||||
index f8108d3d32..1060a3e193 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml
|
||||
@@ -58,13 +58,13 @@ bbbdd7d9df7cdb3d000000000000f03f
|
||||
<stringmatch name="Float print #3" min="1" max="1">floatv3 = -0.001;</stringmatch>
|
||||
<stringmatch name="Float print #4" min="1" max="1">floatv4 = 1e-06;</stringmatch>
|
||||
<stringmatch name="Float print #5" min="1" max="1">floatv5 = INFINITY;</stringmatch>
|
||||
-<stringmatch name="Float print #6" min="1" max="1">floatv6 = -NAN;</stringmatch>
|
||||
+<stringmatch name="Float print #6" min="1" max="1">floatv6 = -?NAN;</stringmatch>
|
||||
<stringmatch name="Float print #7" min="1" max="1">floatv7 = 3.141592e-06;</stringmatch>
|
||||
<stringmatch name="Float print #8" min="1" max="1">double1 = 0.6666666666666666;</stringmatch>
|
||||
<stringmatch name="Float print #9" min="1" max="1">double2 = 2.0;</stringmatch>
|
||||
<stringmatch name="Float print #10" min="1" max="1">double3 = -0.001;</stringmatch>
|
||||
<stringmatch name="Float print #11" min="1" max="1">double4 = 1e-10;</stringmatch>
|
||||
<stringmatch name="Float print #12" min="1" max="1">double5 = INFINITY;</stringmatch>
|
||||
-<stringmatch name="Float print #13" min="1" max="1">double6 = -NAN;</stringmatch>
|
||||
+<stringmatch name="Float print #13" min="1" max="1">double6 = -?NAN;</stringmatch>
|
||||
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.1415926535897933e-06;</stringmatch>
|
||||
</decompilertest>
|
||||
--
|
||||
2.51.1
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
From 0d09640f2d770db6cac6f9a02f5d85373477e5a9 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Kilmer <eric.d.kilmer@gmail.com>
|
||||
Date: Tue, 29 Oct 2024 15:30:57 -0400
|
||||
Subject: [PATCH 4/5] Use string resize instead of reserve
|
||||
|
||||
assign will fix up the size to hold all of what's copied
|
||||
---
|
||||
Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
|
||||
index 5f5fa0c7b3..4cd77156f2 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
|
||||
@@ -67,7 +67,6 @@ void StringManager::assignStringData(StringData &data,const uint1 *buf,int4 size
|
||||
|
||||
{
|
||||
if (charsize == 1 && numChars < maximumChars) {
|
||||
- data.byteData.reserve(size);
|
||||
data.byteData.assign(buf,buf+size);
|
||||
}
|
||||
else {
|
||||
@@ -77,9 +76,9 @@ void StringManager::assignStringData(StringData &data,const uint1 *buf,int4 size
|
||||
return;
|
||||
string resString = s.str();
|
||||
int4 newSize = resString.size();
|
||||
- data.byteData.reserve(newSize + 1);
|
||||
const uint1 *ptr = (const uint1 *)resString.c_str();
|
||||
data.byteData.assign(ptr,ptr+newSize);
|
||||
+ data.byteData.resize(newSize + 1, 0);
|
||||
data.byteData[newSize] = 0; // Make sure there is a null terminator
|
||||
}
|
||||
data.isTruncated = (numChars >= maximumChars);
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
From d9242056b580cc994661c41344c9ad51b636b0e5 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
|
||||
|
||||
This fixes Windows Debug error encountered in testing where it was
|
||||
complaining about lack of strict weak ordering.
|
||||
---
|
||||
Ghidra/Features/Decompiler/src/decompile/cpp/type.cc | 1 +
|
||||
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
|
||||
--- 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
|
||||
|
||||
{
|
||||
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
|
||||
+ if (parent == &op) return 1; // op is our TypeEnum
|
||||
TypePartialEnum *tp = (TypePartialEnum *) &op; // Both must be partial
|
||||
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
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
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,82 +1,22 @@
|
||||
From c5a494bcd940fb46ffe90164c5ec0b61d897a892 Mon Sep 17 00:00:00 2001
|
||||
From 31a52c1f29a0fe67956bbefc1b1b01d6c0ccd5b1 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/5] Fix UBSAN errors in decompiler
|
||||
Subject: [PATCH 1/8] Fix UBSAN errors in decompiler
|
||||
|
||||
Co-authored-by: Alex Cameron <asc@tetsuo.sh>
|
||||
---
|
||||
.../Decompiler/src/decompile/cpp/fspec.cc | 8 ++++++--
|
||||
.../Decompiler/src/decompile/cpp/op.cc | 6 +++++-
|
||||
.../Decompiler/src/decompile/cpp/opbehavior.cc | 8 +++++++-
|
||||
.../src/decompile/cpp/pcodecompile.cc | 18 +++++++++++-------
|
||||
.../Decompiler/src/decompile/cpp/ruleaction.cc | 18 ++++++++++++++----
|
||||
.../Decompiler/src/decompile/cpp/semantics.cc | 2 ++
|
||||
.../Decompiler/src/decompile/cpp/semantics.hh | 2 +-
|
||||
.../src/decompile/cpp/slgh_compile.cc | 2 +-
|
||||
.../Decompiler/src/decompile/cpp/type.cc | 2 +-
|
||||
.../src/decompile/unittests/testfloatemu.cc | 2 +-
|
||||
10 files changed, 49 insertions(+), 19 deletions(-)
|
||||
5 files changed, 16 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc
|
||||
index 6ebec206c9..e5a1ad598c 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc
|
||||
@@ -2868,8 +2868,12 @@ void ProtoModelMerged::decode(Decoder &decoder)
|
||||
modellist.push_back(mymodel);
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
- ((ParamListMerged *)input)->finalize();
|
||||
- ((ParamListMerged *)output)->finalize();
|
||||
+ if (input->getType() == ParamList::p_merged) {
|
||||
+ ((ParamListMerged *)input)->finalize();
|
||||
+ }
|
||||
+ if (output->getType() == ParamList::p_merged) {
|
||||
+ ((ParamListMerged *)output)->finalize();
|
||||
+ }
|
||||
}
|
||||
|
||||
void ParameterBasic::setTypeLock(bool val)
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc
|
||||
index d76a799960..37ba971a34 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc
|
||||
@@ -666,7 +666,11 @@ uintb PcodeOp::getNZMaskLocal(bool cliploop) const
|
||||
break;
|
||||
case CPUI_PIECE:
|
||||
resmask = getIn(0)->getNZMask();
|
||||
- resmask <<= 8*getIn(1)->getSize();
|
||||
+ if (8*getIn(1)->getSize() < sizeof(resmask)) {
|
||||
+ resmask <<= 8*getIn(1)->getSize();
|
||||
+ } else {
|
||||
+ resmask = 0;
|
||||
+ }
|
||||
resmask |= getIn(1)->getNZMask();
|
||||
break;
|
||||
case CPUI_INT_MULT:
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc
|
||||
index aebcfd9103..6c47e6eb15 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc
|
||||
@@ -746,7 +746,13 @@ uintb OpBehaviorPiece::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb i
|
||||
uintb OpBehaviorSubpiece::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const
|
||||
|
||||
{
|
||||
- uintb res = (in1>>(in2*8)) & calc_mask(sizeout);
|
||||
+ uintb res = in1;
|
||||
+ if (in2 < sizeof(in1)) {
|
||||
+ res >>= (in2*8);
|
||||
+ } else {
|
||||
+ res = 0;
|
||||
+ }
|
||||
+ res &= calc_mask(sizeout);
|
||||
return res;
|
||||
}
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
|
||||
index ca9d71ab99..85d4dd281d 100644
|
||||
index a67a3de849..37ba4930e6 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
|
||||
@@ -621,8 +621,10 @@ vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uin
|
||||
@@ -618,8 +618,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;
|
||||
@@ -89,7 +29,7 @@ index ca9d71ab99..85d4dd281d 100644
|
||||
|
||||
if (vn->getSize().getType()==ConstTpl::real) {
|
||||
// If we know the size of the bitranged varnode, we can
|
||||
@@ -726,9 +728,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
@@ -723,9 +725,6 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +39,7 @@ index ca9d71ab99..85d4dd281d 100644
|
||||
if (truncneeded && ((bitoffset % 8)==0)) {
|
||||
truncshift = bitoffset/8;
|
||||
bitoffset = 0;
|
||||
@@ -751,8 +750,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
@@ -748,8 +747,13 @@ ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4
|
||||
appendOp(CPUI_INT_RIGHT,res,bitoffset,4);
|
||||
if (truncneeded)
|
||||
appendOp(CPUI_SUBPIECE,res,truncshift,4);
|
||||
@@ -115,51 +55,8 @@ index ca9d71ab99..85d4dd281d 100644
|
||||
force_size(res->outvn,ConstTpl(ConstTpl::real,finalsize),*res->ops);
|
||||
return res;
|
||||
}
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
|
||||
index f667c6ca95..6ea3916520 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
|
||||
@@ -977,7 +977,12 @@ int4 RulePullsubIndirect::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Varnode *outvn = op->getOut();
|
||||
if (outvn->isPrecisLo()||outvn->isPrecisHi()) return 0; // Don't pull apart double precision object
|
||||
|
||||
- uintb consume = calc_mask(newSize) << 8 * minByte;
|
||||
+ uintb consume = calc_mask(newSize);
|
||||
+ if (8 * minByte < sizeof(consume)) {
|
||||
+ consume <<= 8 * minByte;
|
||||
+ } else {
|
||||
+ consume = 0;
|
||||
+ }
|
||||
consume = ~consume;
|
||||
if ((consume & indir->getIn(0)->getConsume())!=0) return 0;
|
||||
|
||||
@@ -7026,8 +7031,9 @@ int4 RulePtrsubCharConstant::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Varnode *sb = op->getIn(0);
|
||||
Datatype *sbType = sb->getTypeReadFacing(op);
|
||||
if (sbType->getMetatype() != TYPE_PTR) return 0;
|
||||
- TypeSpacebase *sbtype = (TypeSpacebase *)((TypePointer *)sbType)->getPtrTo();
|
||||
- if (sbtype->getMetatype() != TYPE_SPACEBASE) return 0;
|
||||
+ Datatype *sbTypePtr = ((TypePointer *)sbType)->getPtrTo();
|
||||
+ if (sbTypePtr->getMetatype() != TYPE_SPACEBASE) return 0;
|
||||
+ TypeSpacebase *sbtype = (TypeSpacebase *)sbTypePtr;
|
||||
Varnode *vn1 = op->getIn(1);
|
||||
if (!vn1->isConstant()) return 0;
|
||||
Varnode *outvn = op->getOut();
|
||||
@@ -8831,7 +8837,11 @@ int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Varnode *outvn = op->getOut();
|
||||
int4 flowsize = outvn->getSize();
|
||||
uintb mask = calc_mask( flowsize );
|
||||
- mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||
+ if (8*((int4)op->getIn(1)->getOffset()) < sizeof(mask)) {
|
||||
+ mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||
+ } else {
|
||||
+ mask = 0;
|
||||
+ }
|
||||
bool aggressive = outvn->isPtrFlow();
|
||||
if (!aggressive) {
|
||||
if ((vn->getConsume() & mask) != vn->getConsume()) return 0;
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc
|
||||
index cd9b9835b1..8a4616c3b9 100644
|
||||
index 18e2ff8ba1..3bfe29f2ef 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)
|
||||
@@ -179,7 +76,7 @@ index cd9b9835b1..8a4616c3b9 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 e0b069959d..9117a45c75 100644
|
||||
index b53b18797d..b2f043e32d 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 {
|
||||
@@ -192,10 +89,10 @@ index e0b069959d..9117a45c75 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 50d85e22ba..9f3b456229 100644
|
||||
index 75bebffcb0..bf5e7ce681 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc
|
||||
@@ -2164,8 +2164,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
|
||||
@@ -2323,8 +2323,8 @@ string SleighCompile::checkSymbols(SymbolScope *scope)
|
||||
ostringstream msg;
|
||||
SymbolTree::const_iterator iter;
|
||||
for(iter=scope->begin();iter!=scope->end();++iter) {
|
||||
@@ -205,20 +102,6 @@ index 50d85e22ba..9f3b456229 100644
|
||||
if (sym->getRefCount() == 0)
|
||||
msg << " Label <" << sym->getName() << "> was placed but not used" << endl;
|
||||
else if (!sym->isPlaced())
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
|
||||
index 7cda43127f..20a5bc1c83 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc
|
||||
@@ -3661,8 +3661,8 @@ void TypeFactory::recalcPointerSubmeta(Datatype *base,sub_metatype sub)
|
||||
top.submeta = sub; // Search on the incorrect submeta
|
||||
iter = tree.lower_bound(&top);
|
||||
while(iter != tree.end()) {
|
||||
+ if ((*iter)->getMetatype() != TYPE_PTR) break;
|
||||
TypePointer *ptr = (TypePointer *)*iter;
|
||||
- if (ptr->getMetatype() != TYPE_PTR) break;
|
||||
if (ptr->ptrto != base) break;
|
||||
++iter;
|
||||
if (ptr->submeta == sub) {
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testfloatemu.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testfloatemu.cc
|
||||
index 2571f55f1a..fe40e22b1b 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/unittests/testfloatemu.cc
|
||||
@@ -233,5 +116,5 @@ index 2571f55f1a..fe40e22b1b 100644
|
||||
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
|
||||
uintb encoding = format.getEncoding(f);
|
||||
--
|
||||
2.47.0
|
||||
2.54.0
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 60b1bb423c10b2e53ad72e7b84ac647e56282aca Mon Sep 17 00:00:00 2001
|
||||
From 0928d2c8e97646042265cee1d2752514d02d3d4f 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/5] Use `stroull` instead of `stroul` to parse address
|
||||
Subject: [PATCH 2/8] 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.47.0
|
||||
2.54.0
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
From e853cd1dc018c0f8a76f936de0391bbdf467a907 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Kilmer <eric.d.kilmer@gmail.com>
|
||||
Date: Sat, 30 Mar 2024 18:46:16 -0400
|
||||
Subject: [PATCH 3/5] Add missing index check to prevent errors in Windows
|
||||
|
||||
Not sure why this only appears when testing on Windows.
|
||||
---
|
||||
Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
|
||||
index 0237adb820..5048a9d480 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
|
||||
@@ -1844,7 +1844,7 @@ void IfcProtooverride::execute(istream &s)
|
||||
s >> ws;
|
||||
Address callpoint(parse_machaddr(s,discard,*dcp->conf->types));
|
||||
int4 i;
|
||||
- for(i=0;dcp->fd->numCalls();++i)
|
||||
+ for(i=0;i<dcp->fd->numCalls();++i)
|
||||
if (dcp->fd->getCallSpecs(i)->getOp()->getAddr() == callpoint) break;
|
||||
if (i == dcp->fd->numCalls())
|
||||
throw IfaceExecutionError("No call is made at this address");
|
||||
--
|
||||
2.47.0
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
From 5fe91a722d58d4852f5ffadb420eb81627fd914e Mon Sep 17 00:00:00 2001
|
||||
From e08c2cb8c6f49a2127b14ba081f1a5ec600c3cea 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 5/5] Ignore floating point test due to compilation differences
|
||||
Subject: [PATCH 3/8] 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.47.0
|
||||
2.54.0
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
From b5500258acdee1b2f5dfe603b1da33a7eebf699f 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
|
||||
point test
|
||||
|
||||
At least on Apple Silicon, this test reports positive NAN.
|
||||
|
||||
Use the regex optional operator '?' to allow for positive or negative
|
||||
NAN.
|
||||
---
|
||||
.../Decompiler/src/decompile/datatests/floatprint.xml | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml
|
||||
index f8108d3d32..1060a3e193 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/datatests/floatprint.xml
|
||||
@@ -58,13 +58,13 @@ bbbdd7d9df7cdb3d000000000000f03f
|
||||
<stringmatch name="Float print #3" min="1" max="1">floatv3 = -0.001;</stringmatch>
|
||||
<stringmatch name="Float print #4" min="1" max="1">floatv4 = 1e-06;</stringmatch>
|
||||
<stringmatch name="Float print #5" min="1" max="1">floatv5 = INFINITY;</stringmatch>
|
||||
-<stringmatch name="Float print #6" min="1" max="1">floatv6 = -NAN;</stringmatch>
|
||||
+<stringmatch name="Float print #6" min="1" max="1">floatv6 = -?NAN;</stringmatch>
|
||||
<stringmatch name="Float print #7" min="1" max="1">floatv7 = 3.141592e-06;</stringmatch>
|
||||
<stringmatch name="Float print #8" min="1" max="1">double1 = 0.6666666666666666;</stringmatch>
|
||||
<stringmatch name="Float print #9" min="1" max="1">double2 = 2.0;</stringmatch>
|
||||
<stringmatch name="Float print #10" min="1" max="1">double3 = -0.001;</stringmatch>
|
||||
<stringmatch name="Float print #11" min="1" max="1">double4 = 1e-10;</stringmatch>
|
||||
<stringmatch name="Float print #12" min="1" max="1">double5 = INFINITY;</stringmatch>
|
||||
-<stringmatch name="Float print #13" min="1" max="1">double6 = -NAN;</stringmatch>
|
||||
+<stringmatch name="Float print #13" min="1" max="1">double6 = -?NAN;</stringmatch>
|
||||
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.1415926535897933e-06;</stringmatch>
|
||||
</decompilertest>
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
From ddd126b14277841a885e824047908fb360e26191 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Kilmer <eric.d.kilmer@gmail.com>
|
||||
Date: Tue, 29 Oct 2024 15:30:57 -0400
|
||||
Subject: [PATCH 4/5] Use string resize instead of reserve
|
||||
|
||||
assign will fix up the size to hold all of what's copied
|
||||
---
|
||||
Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
|
||||
index 5f5fa0c7b3..4cd77156f2 100644
|
||||
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
|
||||
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/stringmanage.cc
|
||||
@@ -67,7 +67,6 @@ void StringManager::assignStringData(StringData &data,const uint1 *buf,int4 size
|
||||
|
||||
{
|
||||
if (charsize == 1 && numChars < maximumChars) {
|
||||
- data.byteData.reserve(size);
|
||||
data.byteData.assign(buf,buf+size);
|
||||
}
|
||||
else {
|
||||
@@ -77,9 +76,9 @@ void StringManager::assignStringData(StringData &data,const uint1 *buf,int4 size
|
||||
return;
|
||||
string resString = s.str();
|
||||
int4 newSize = resString.size();
|
||||
- data.byteData.reserve(newSize + 1);
|
||||
const uint1 *ptr = (const uint1 *)resString.c_str();
|
||||
data.byteData.assign(ptr,ptr+newSize);
|
||||
+ data.byteData.resize(newSize + 1, 0);
|
||||
data.byteData[newSize] = 0; // Make sure there is a null terminator
|
||||
}
|
||||
data.isTruncated = (numChars >= maximumChars);
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
From 6d5dbe6ffcfc31468ebbf2299ea72f5ef8abcf15 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
|
||||
|
||||
This fixes Windows Debug error encountered in testing where it was
|
||||
complaining about lack of strict weak ordering.
|
||||
---
|
||||
Ghidra/Features/Decompiler/src/decompile/cpp/type.cc | 1 +
|
||||
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
|
||||
--- 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
|
||||
|
||||
{
|
||||
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
|
||||
+ if (parent == &op) return 1; // op is our TypeEnum
|
||||
TypePartialEnum *tp = (TypePartialEnum *) &op; // Both must be partial
|
||||
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
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
@@ -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 "11.2")
|
||||
set(ghidra_version "12.1.2")
|
||||
set(ghidra_git_tag "Ghidra_${ghidra_version}_build")
|
||||
set(ghidra_shallow TRUE)
|
||||
|
||||
@@ -37,21 +37,25 @@ 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-Add-missing-index-check-to-prevent-errors-in-Windows.patch"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0004-Use-string-resize-instead-of-reserve.patch"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0005-Ignore-floating-point-test-due-to-compilation-differ.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 "11.3")
|
||||
set(ghidra_head_version "12.2")
|
||||
set(ghidra_version "${ghidra_head_version}")
|
||||
set(ghidra_head_git_tag "fd1a448bf517939b5f3b7e369a8c10e8b1b22136")
|
||||
set(ghidra_head_git_tag "74d498f8da7d13f84604f531d59cb0cac028d6b2")
|
||||
set(ghidra_git_tag "${ghidra_head_git_tag}")
|
||||
set(ghidra_shallow FALSE)
|
||||
set(ghidra_patches
|
||||
@@ -60,9 +64,13 @@ if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
|
||||
"${GIT_EXECUTABLE}" am --ignore-space-change --ignore-whitespace --no-gpg-sign
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0001-Fix-UBSAN-errors-in-decompiler.patch"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0002-Use-stroull-instead-of-stroul-to-parse-address-offse.patch"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0003-Add-missing-index-check-to-prevent-errors-in-Windows.patch"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0004-Use-string-resize-instead-of-reserve.patch"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0005-Ignore-floating-point-test-due-to-compilation-differ.patch"
|
||||
"${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()
|
||||
@@ -109,10 +117,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"
|
||||
@@ -172,11 +180,14 @@ set(sleigh_deccore_source_list
|
||||
"${library_root}/signature.cc"
|
||||
"${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"
|
||||
@@ -193,6 +204,10 @@ 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"
|
||||
|
||||
@@ -4,11 +4,13 @@ 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"
|
||||
@@ -58,6 +60,7 @@ 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"
|
||||
@@ -76,6 +79,8 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/MIPS/data/languages/mips32le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/MIPS/data/languages/mips64be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/MIPS/data/languages/mips64le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/NDS32/data/languages/nds32be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/NDS32/data/languages/nds32le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PA-RISC/data/languages/pa-risc32be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PIC/data/languages/PIC24E.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PIC/data/languages/PIC24F.slaspec"
|
||||
@@ -108,6 +113,7 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PowerPC/data/languages/ppc_64_isa_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PowerPC/data/languages/ppc_64_isa_vle_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PowerPC/data/languages/ppc_64_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/RISCV/data/languages/andestar_v5.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/RISCV/data/languages/riscv.ilp32d.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/RISCV/data/languages/riscv.lp64d.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Sparc/data/languages/SparcV9_32.slaspec"
|
||||
@@ -121,6 +127,7 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/TI_MSP430/data/languages/TI_MSP430X.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_be_harvard.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_be_harvard_rev.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy_be_posStack.slaspec"
|
||||
@@ -136,6 +143,7 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Xtensa/data/languages/xtensa_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Z80/data/languages/z180.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Z80/data/languages/z80.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/eBPF/data/languages/eBPF_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/eBPF/data/languages/eBPF_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/tricore/data/languages/tricore.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/x86/data/languages/x86-64.slaspec"
|
||||
|
||||
@@ -9,6 +9,7 @@ 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"
|
||||
@@ -58,6 +59,7 @@ 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"
|
||||
@@ -76,6 +78,8 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/MIPS/data/languages/mips32le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/MIPS/data/languages/mips64be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/MIPS/data/languages/mips64le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/NDS32/data/languages/nds32be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/NDS32/data/languages/nds32le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PA-RISC/data/languages/pa-risc32be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PIC/data/languages/PIC24E.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PIC/data/languages/PIC24F.slaspec"
|
||||
@@ -108,6 +112,7 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PowerPC/data/languages/ppc_64_isa_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PowerPC/data/languages/ppc_64_isa_vle_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/PowerPC/data/languages/ppc_64_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/RISCV/data/languages/andestar_v5.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/RISCV/data/languages/riscv.ilp32d.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/RISCV/data/languages/riscv.lp64d.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Sparc/data/languages/SparcV9_32.slaspec"
|
||||
@@ -121,6 +126,7 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/TI_MSP430/data/languages/TI_MSP430X.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_be_harvard.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_be_harvard_rev.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy64_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Toy/data/languages/toy_be_posStack.slaspec"
|
||||
@@ -136,6 +142,7 @@ set(spec_file_list
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Xtensa/data/languages/xtensa_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Z80/data/languages/z180.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/Z80/data/languages/z80.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/eBPF/data/languages/eBPF_be.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/eBPF/data/languages/eBPF_le.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/tricore/data/languages/tricore.slaspec"
|
||||
"${ghidrasource_SOURCE_DIR}/Ghidra/Processors/x86/data/languages/x86-64.slaspec"
|
||||
|
||||
+15
-14
@@ -10,7 +10,7 @@
|
||||
# Tests from ghidra repo
|
||||
#
|
||||
|
||||
add_executable(sleigh_ghidra_test
|
||||
add_executable(sleigh_decomp_test
|
||||
${sleigh_core_source_list}
|
||||
${sleigh_deccore_source_list}
|
||||
${sleigh_source_list}
|
||||
@@ -26,32 +26,33 @@ add_executable(sleigh_ghidra_test
|
||||
"${library_root}/../unittests/testparamstore.cc"
|
||||
)
|
||||
|
||||
# if(sleigh_RELEASE_IS_HEAD)
|
||||
# target_sources(sleigh_ghidra_test PRIVATE
|
||||
# )
|
||||
# endif()
|
||||
if(sleigh_RELEASE_IS_HEAD)
|
||||
target_sources(sleigh_decomp_test PRIVATE
|
||||
"${library_root}/../unittests/testmultiprec.cc"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_features(sleigh_ghidra_test PRIVATE cxx_std_11)
|
||||
target_include_directories(sleigh_ghidra_test PRIVATE "${library_root}")
|
||||
target_compile_features(sleigh_decomp_test PRIVATE cxx_std_11)
|
||||
target_include_directories(sleigh_decomp_test PRIVATE "${library_root}")
|
||||
include(CheckIncludeFileCXX)
|
||||
check_include_file_cxx(termios.h HAVE_TERMIOS_H)
|
||||
if(HAVE_TERMIOS_H)
|
||||
target_compile_definitions(sleigh_ghidra_test PRIVATE
|
||||
target_compile_definitions(sleigh_decomp_test PRIVATE
|
||||
__TERMINAL__
|
||||
)
|
||||
endif()
|
||||
sleigh_add_optional_defines(sleigh_ghidra_test PRIVATE)
|
||||
sleigh_add_optional_defines(sleigh_decomp_test PRIVATE)
|
||||
|
||||
target_link_libraries(sleigh_ghidra_test PRIVATE ZLIB::ZLIB)
|
||||
target_link_libraries(sleigh_decomp_test PRIVATE ZLIB::ZLIB)
|
||||
|
||||
add_test(
|
||||
NAME sleigh_ghidra_unittest
|
||||
COMMAND sleigh_ghidra_test -sleighpath "${PROJECT_BINARY_DIR}" unittests
|
||||
NAME sleigh_decomp_unittest
|
||||
COMMAND sleigh_decomp_test -sleighpath "${PROJECT_BINARY_DIR}" unittests
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME sleigh_ghidra_datatest
|
||||
COMMAND sleigh_ghidra_test -sleighpath "${PROJECT_BINARY_DIR}"
|
||||
NAME sleigh_decomp_datatest
|
||||
COMMAND sleigh_decomp_test -sleighpath "${PROJECT_BINARY_DIR}"
|
||||
-path "${library_root}/../datatests"
|
||||
datatests
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user