Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6a9c300
build: add ida-cobra plugin CMake scaffolding
kyle-elliott-tob Apr 3, 2026
497db80
feat(ida): add MicrocodeDetector for MBA tree detection
kyle-elliott-tob Apr 3, 2026
f12d019
feat(ida): add MicrocodeConverter for minsn_t <-> Expr conversion
kyle-elliott-tob Apr 3, 2026
e8d688d
feat(ida): add Verifier for random-input equivalence checks
kyle-elliott-tob Apr 3, 2026
43c1c4c
feat(ida): add plugin entry point with Hex-Rays callbacks and config
kyle-elliott-tob Apr 3, 2026
4a85e52
ci: add IDA plugin build job (Linux/macOS/Windows)
kyle-elliott-tob Apr 3, 2026
edfc90f
feat(ida): add Tier 2 cross-block detection scaffold with post-order …
kyle-elliott-tob Apr 3, 2026
3169936
fix(ida): address code review findings
kyle-elliott-tob Apr 3, 2026
06c37de
ci: add release job to publish IDA plugin artifacts on tags
kyle-elliott-tob Apr 3, 2026
862b376
fix(ci): replace third-party ninja action with package manager installs
kyle-elliott-tob Apr 3, 2026
272ddf8
fix(ci): gate LLVM dep behind COBRA_ENABLE_LLVM, default OFF
kyle-elliott-tob Apr 3, 2026
b0000eb
fix(ci): use --unresolved-symbols=ignore-all for Linux IDA plugin lin…
kyle-elliott-tob Apr 3, 2026
3020936
fix(ci): only upload IDA plugin artifacts on tag releases
kyle-elliott-tob Apr 3, 2026
26fc86c
fix(ci): add IDA SDK platform defines, use clang-cl on Windows
kyle-elliott-tob Apr 3, 2026
793e332
fix(cmake): fix library suffix for macos
Ninja3047 Apr 3, 2026
77ed298
fix(ida): use value equality for mop_t matching
Ninja3047 Apr 3, 2026
eced60d
fix(ida): drill past wrapper opcodes to find MBA root
Ninja3047 Apr 3, 2026
7300f27
refactor(ida): convert recursive tree walks to iterative
Ninja3047 Apr 3, 2026
ad377c2
Merge pull request #15 from trailofbits/fixes-and-refactor
kyle-elliott-tob Apr 3, 2026
55d7c19
feat(ida): tag simplified functions via netnode for script queries
Ninja3047 Apr 3, 2026
e43d1d2
feat(ida): add headless idalib test script for CoBRA
Ninja3047 Apr 3, 2026
1e2ae83
style(ida): apply clang-format to IDA plugin sources
Ninja3047 Apr 3, 2026
50a8467
style(ida): reformat with clang-format-22 to match CI
Ninja3047 Apr 3, 2026
b25ff31
test: add scalar extension lowering tests (red)
kyle-elliott-tob Apr 3, 2026
f69f40d
feat: add shared scalar extension lowering helpers
kyle-elliott-tob Apr 3, 2026
e6d0b74
test: add Expr extension lowering tests
kyle-elliott-tob Apr 3, 2026
a1ebc47
refactor(llvm): split IsMbaOpcode into IsCoreMbaOpcode + IsTreeOpcode
kyle-elliott-tob Apr 3, 2026
257df1c
feat(llvm): lower ZExt/SExt via shared extension helpers
kyle-elliott-tob Apr 3, 2026
7ce29a7
test(llvm): add FileCheck tests for extension lowering
kyle-elliott-tob Apr 3, 2026
b9dcff6
fix(ida): peel only m_mov in MbaRoot, derive bitwidth from root
kyle-elliott-tob Apr 3, 2026
e4db6be
fix(ida): validate extension widths in LeafCollector
kyle-elliott-tob Apr 3, 2026
a33cc21
feat(ida): lower xdu/xds via shared extension helpers
kyle-elliott-tob Apr 3, 2026
78ac0e7
fix(ida): validate root extension widths before signature generation
kyle-elliott-tob Apr 4, 2026
c543e4f
fix: address review findings — semantic test and dead code
kyle-elliott-tob Apr 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: CI
on:
push:
branches: [master]
tags: ['v*']
pull_request:
branches: [master]

Expand Down Expand Up @@ -80,6 +81,7 @@ jobs:
-DCMAKE_C_COMPILER="${CC:-${{ matrix.cc }}}" \
-DCMAKE_CXX_COMPILER="${CXX:-${{ matrix.cxx }}}" \
-DCMAKE_BUILD_TYPE=Release \
-DCOBRA_ENABLE_LLVM=ON \
-DUSE_EXTERNAL_LLVM=ON \
-DCOBRA_BUILD_TESTS=ON
cmake --build build-deps
Expand Down Expand Up @@ -117,3 +119,141 @@ jobs:
run: |
find include lib tools \( -name '*.h' -o -name '*.cpp' \) -print0 \
| xargs -0 clang-format-22 --dry-run --Werror

ida-plugin:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
cc: gcc-14
cxx: g++-14
name: Linux
- os: macos-15
cc: clang
cxx: clang++
name: macOS
- os: windows-latest
name: Windows

name: IDA Plugin (${{ matrix.name }})
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Clone IDA SDK
run: >
git clone --depth 1 --branch v9.3.0-sdk.3
https://github.com/HexRaysSA/ida-sdk.git ida-sdk

- name: Install Ninja (Linux)
if: runner.os == 'Linux'
run: sudo apt-get install -y -qq ninja-build

- name: Install Ninja (macOS)
if: runner.os == 'macOS'
run: brew install ninja

- name: Install Ninja (Windows)
if: runner.os == 'Windows'
run: choco install ninja -y

- name: Cache dependencies
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: build-deps/install
key: deps-ida-${{ matrix.os }}-${{ hashFiles('dependencies/*.cmake') }}

- name: Build dependencies (Unix)
if: runner.os != 'Windows'
run: |
cmake -S dependencies -B build-deps -G Ninja \
-DCMAKE_C_COMPILER="${CC:-${{ matrix.cc }}}" \
-DCMAKE_CXX_COMPILER="${CXX:-${{ matrix.cxx }}}" \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-deps

- name: Build dependencies (Windows)
if: runner.os == 'Windows'
run: |
cmake -S dependencies -B build-deps -G Ninja `
-DCMAKE_C_COMPILER=clang-cl `
-DCMAKE_CXX_COMPILER=clang-cl `
-DCMAKE_BUILD_TYPE=Release
cmake --build build-deps

- name: Build IDA plugin (Unix)
if: runner.os != 'Windows'
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_C_COMPILER="${CC:-${{ matrix.cc }}}" \
-DCMAKE_CXX_COMPILER="${CXX:-${{ matrix.cxx }}}" \
-DCMAKE_PREFIX_PATH="$(pwd)/build-deps/install" \
-DCMAKE_BUILD_TYPE=Release \
-DCOBRA_BUILD_IDA_PLUGIN=ON \
-DIDA_SDK_DIR="$(pwd)/ida-sdk/src"
cmake --build build --target ida-cobra

- name: Build IDA plugin (Windows)
if: runner.os == 'Windows'
run: |
cmake -S . -B build -G Ninja `
-DCMAKE_C_COMPILER=clang-cl `
-DCMAKE_CXX_COMPILER=clang-cl `
-DCMAKE_PREFIX_PATH="$(Resolve-Path build-deps/install)" `
-DCMAKE_BUILD_TYPE=Release `
-DCOBRA_BUILD_IDA_PLUGIN=ON `
-DIDA_SDK_DIR="$(Resolve-Path ida-sdk/src)"
cmake --build build --target ida-cobra

- name: Upload plugin artifact
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ida-cobra-${{ matrix.name }}
path: |
build/lib/ida/ida-cobra.*
lib/ida/ida-cobra.cfg

release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [build, lint, ida-plugin]
runs-on: ubuntu-24.04
permissions:
contents: write

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Download all IDA plugin artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdceab917 # v4.3.0
with:
pattern: ida-cobra-*
path: release-artifacts/

- name: Rename artifacts with version and platform
run: |
version="${GITHUB_REF_NAME#v}"
mkdir -p release
cp lib/ida/ida-cobra.cfg "release/ida-cobra.cfg"
for dir in release-artifacts/ida-cobra-*; do
platform="$(echo "$(basename "$dir" | sed 's/ida-cobra-//')" | tr '[:upper:]' '[:lower:]')"
for bin in "$dir"/build/lib/ida/ida-cobra.*; do
ext="${bin##*.}"
cp "$bin" "release/ida-cobra-${platform}-${version}.${ext}"
done
done

- name: Upload to GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${{ github.ref_name }}" \
--title "${{ github.ref_name }}" \
--generate-notes \
release/*
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ option(COBRA_BUILD_LLVM_PASS "Build the LLVM pass plugin (requires LLVM 19-22)"
option(COBRA_BUILD_TESTS "Build tests (requires GoogleTest in prefix)" OFF)
option(COBRA_ENABLE_TRACE "Enable detailed pipeline tracing to stderr (debug builds)" OFF)
option(COBRA_ENABLE_TRACY "Enable Tracy profiler instrumentation" OFF)
option(COBRA_BUILD_IDA_PLUGIN "Build IDA Pro plugin (requires IDA_SDK_DIR)" OFF)

if(COBRA_BUILD_LLVM_PASS)
find_package(LLVM REQUIRED CONFIG)
Expand Down Expand Up @@ -93,6 +94,10 @@ if(COBRA_BUILD_LLVM_PASS)
add_subdirectory(lib/llvm)
endif()

if(COBRA_BUILD_IDA_PLUGIN)
add_subdirectory(lib/ida)
endif()

if(COBRA_BUILD_TESTS)
enable_testing()
add_subdirectory(test)
Expand Down
7 changes: 6 additions & 1 deletion dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# cmake --build build-deps
#
# Options:
# COBRA_ENABLE_LLVM (default OFF) - Enable LLVM dependency
# USE_EXTERNAL_LLVM (default ON) - Use system LLVM vs build from source
# USE_EXTERNAL_HIGHWAY (default OFF) - Use system highway vs build from source
# COBRA_BUILD_TESTS (default OFF) - Build GoogleTest for tests
Expand All @@ -16,6 +17,7 @@
cmake_minimum_required(VERSION 3.20)
project(cobra-dependencies LANGUAGES C CXX)

option(COBRA_ENABLE_LLVM "Enable LLVM dependency" OFF)
option(USE_EXTERNAL_LLVM "Use system LLVM instead of building from source" ON)
option(USE_EXTERNAL_ABSEIL "Use system abseil instead of building from source" OFF)
option(USE_EXTERNAL_HIGHWAY "Use system highway instead of building from source" OFF)
Expand All @@ -28,7 +30,10 @@ include(superbuild.cmake)

include(abseil.cmake)
include(highway.cmake)
include(llvm.cmake)

if(COBRA_ENABLE_LLVM)
include(llvm.cmake)
endif()

if(COBRA_BUILD_TESTS)
include(googletest.cmake)
Expand Down
24 changes: 24 additions & 0 deletions include/cobra/core/ExtensionLowering.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include "cobra/core/Expr.h"

#include <cstdint>
#include <memory>

namespace cobra {

// Scalar helpers — used by frontend evaluators.
// Precondition: 1 <= source_bits <= 64.
uint64_t EvalZeroExtend(uint64_t val, uint32_t source_bits, uint64_t result_mask);
uint64_t EvalSignExtend(uint64_t val, uint32_t source_bits, uint64_t result_mask);

// Expr helpers — used by frontend AST builders.
// Return ordinary fixed-width Expr; no new Expr::Kind values.
// Precondition: 1 <= source_bits <= 64.
// When source_bits == 64, returns inner unchanged (identity).
std::unique_ptr< Expr >
LowerZeroExtend(std::unique_ptr< Expr > inner, uint32_t source_bits);
std::unique_ptr< Expr >
LowerSignExtend(std::unique_ptr< Expr > inner, uint32_t source_bits);

} // namespace cobra
1 change: 1 addition & 0 deletions lib/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ target_sources(cobra-core PRIVATE
SignatureEval.cpp
MixedProductRewriter.cpp
ExprCost.cpp
ExtensionLowering.cpp
SignatureSimplifier.cpp
BitwiseDecomposer.cpp
HybridDecomposer.cpp
Expand Down
57 changes: 57 additions & 0 deletions lib/core/ExtensionLowering.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "cobra/core/ExtensionLowering.h"

#include "cobra/core/BitWidth.h"

#include <cassert>

namespace cobra {
namespace {

struct ExtMasks
{
uint64_t low_mask;
uint64_t sign_bit;
};

ExtMasks ComputeExtMasks(uint32_t source_bits) {
assert(source_bits >= 1 && source_bits <= 64);
return ExtMasks{
.low_mask = Bitmask(source_bits),
.sign_bit = 1ULL << (source_bits - 1),
};
}

} // anonymous namespace

uint64_t EvalZeroExtend(uint64_t val, uint32_t source_bits, uint64_t result_mask) {
auto [low_mask, sign_bit] = ComputeExtMasks(source_bits);
return (val & low_mask) & result_mask;
}

uint64_t EvalSignExtend(uint64_t val, uint32_t source_bits, uint64_t result_mask) {
auto [low_mask, sign_bit] = ComputeExtMasks(source_bits);
uint64_t masked = val & low_mask;
return ((masked ^ sign_bit) - sign_bit) & result_mask;
}

std::unique_ptr< Expr >
LowerZeroExtend(std::unique_ptr< Expr > inner, uint32_t source_bits) {
assert(source_bits >= 1 && source_bits <= 64);
if (source_bits == 64) { return inner; }

auto [low_mask, sign_bit] = ComputeExtMasks(source_bits);
return Expr::BitwiseAnd(std::move(inner), Expr::Constant(low_mask));
}

std::unique_ptr< Expr >
LowerSignExtend(std::unique_ptr< Expr > inner, uint32_t source_bits) {
assert(source_bits >= 1 && source_bits <= 64);
if (source_bits == 64) { return inner; }

auto [low_mask, sign_bit] = ComputeExtMasks(source_bits);
auto masked = Expr::BitwiseAnd(std::move(inner), Expr::Constant(low_mask));
auto xored = Expr::BitwiseXor(std::move(masked), Expr::Constant(sign_bit));
return Expr::Add(std::move(xored), Expr::Negate(Expr::Constant(sign_bit)));
}

} // namespace cobra
64 changes: 64 additions & 0 deletions lib/ida/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
if(NOT DEFINED IDA_SDK_DIR)
message(FATAL_ERROR
"IDA_SDK_DIR must be set when COBRA_BUILD_IDA_PLUGIN is ON.\n"
" cmake -DIDA_SDK_DIR=/path/to/ida-sdk/src ...")
endif()

# Detect platform-specific IDA stub library directory
if(APPLE)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(_ida_lib_suffix "arm64_mac_64")
else()
set(_ida_lib_suffix "x64_mac_64")
endif()
set(_ida_lib_name "libida.dylib")
elseif(WIN32)
set(_ida_lib_suffix "x64_win_64")
set(_ida_lib_name "ida.lib")
else()
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
set(_ida_lib_suffix "arm64_linux_64")
else()
set(_ida_lib_suffix "x64_linux_64")
endif()
set(_ida_lib_name "libida.so")
endif()

set(_ida_lib_path "${IDA_SDK_DIR}/lib/${_ida_lib_suffix}/${_ida_lib_name}")
if(NOT EXISTS "${_ida_lib_path}")
message(FATAL_ERROR "IDA stub library not found: ${_ida_lib_path}")
endif()

add_library(ida-cobra MODULE
ida-cobra.cpp
MicrocodeDetector.cpp
MicrocodeConverter.cpp
Verifier.cpp
)

target_link_libraries(ida-cobra PRIVATE cobra-core "${_ida_lib_path}")

target_include_directories(ida-cobra PRIVATE
${IDA_SDK_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
)

# IDA SDK platform defines
target_compile_definitions(ida-cobra PRIVATE
__EA64__=1
$<$<PLATFORM_ID:Windows>:__NT__>
$<$<PLATFORM_ID:Linux>:__LINUX__>
$<$<PLATFORM_ID:Darwin>:__MAC__>
)

set_target_properties(ida-cobra PROPERTIES
PREFIX ""
SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}"
)

install(TARGETS ida-cobra
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/cobra
)
install(FILES ida-cobra.cfg
DESTINATION ${CMAKE_INSTALL_DATADIR}/cobra
)
Loading
Loading