Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Binary file added tests/i386/ccop_triggers/ccop_adc_sbb_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_adc_sbb_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_adc_sbb_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_add_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_add_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_add_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_copy_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_copy_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_copy_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_inc_dec_O1_incdec
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_inc_dec_O2_haswell
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_inc_dec_Os_incdec
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_logic_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_logic_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_logic_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_rflags_c_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_rflags_c_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_rflags_c_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_shl_shr_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_shl_shr_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_shl_shr_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_sub_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_sub_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_sub_Os
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_umul_smul_O1
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_umul_smul_O2
Binary file not shown.
Binary file added tests/i386/ccop_triggers/ccop_umul_smul_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_adc_sbb_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_adc_sbb_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_adc_sbb_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_add_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_add_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_add_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_copy_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_copy_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_copy_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_inc_dec_O1_incdec
Binary file not shown.
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_inc_dec_Os_incdec
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_logic_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_logic_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_logic_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_rflags_c_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_rflags_c_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_rflags_c_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_shl_shr_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_shl_shr_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_shl_shr_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_sub_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_sub_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_sub_Os
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_umul_smul_O1
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_umul_smul_O2
Binary file not shown.
Binary file added tests/x86_64/ccop_triggers/ccop_umul_smul_Os
Binary file not shown.
259 changes: 259 additions & 0 deletions tests_src/ccop_triggers/STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# ccop_triggers Test Suite — Status

## What This Is

A set of C source files designed to trigger every VEX condition-code operation (ccop) type
handled by angr's decompiler ccall rewriters (`amd64_ccalls.py`, `x86_ccalls.py`). Each
function is `noinline`, uses a volatile sink to prevent dead-code elimination, and is compiled
with `-fno-if-conversion` to force real branches (not cmov).

Built binaries go into `bin/amd64/` and `bin/i386/` (gitignored). Run `bash build.sh` to
rebuild.

## Current State: All 9 source files complete, all P0/P1 gaps filled

### Complete — ~285 functions across 54 binaries (all build cleanly, all tests pass)

| File | Functions | Notes |
|------|-----------|-------|
| `ccop_sub.c` | 48 | SUB/CMP: CondZ/NZ/L/NL/LE/NLE/B/NB/BE/NBE/S/NS all at 8,16,32,64. Pure C comparisons. |
| `ccop_add.c` | 39 | ADD: CondZ/NZ/S/NS/O/NO/B (8,16,32,64), CondLE/NLE (8,16,32,64), CondBE (8,16,32). Uses `__builtin_add_overflow` for CondO; `asm goto` for CondLE/NLE/BE. 64-bit CondLE/NLE guarded by `__x86_64__`. |
| `ccop_logic.c` | 38 | LOGIC (test/and): CondZ/NZ/S/NS/L/NL (8,16,32,64), CondLE/NLE (8,16,32,64), CondB/BE (8,16,32 x86 only via `asm goto`). |
| `ccop_inc_dec.c` | 48 | INC: CondZ/NZ/S/NS (8,16,32,64), CondO/NO (8,16,32 via asm goto), CondLE/NLE (32 fallback). DEC: CondZ/NZ/S/NS/LE/NLE (8,16,32,64). All inline asm; built with `-mtune-ctrl=use_incdec`. |
| `ccop_shl_shr.c` | 32 | SHL: CondZ/NZ/S/NS (8,16,32,64). SHR: CondZ/NZ/S/NS (8,16,32,64). Pure C shifts; narrow widths use volatile + explicit casts. |
| `ccop_umul_smul.c` | 16 | UMUL/SMUL: CondO/NO (8,16,32,64). Uses `__builtin_*_overflow`. 64-bit UMUL CondNO guarded by `__x86_64__`. |
| `ccop_adc_sbb.c` | ~59 | ADC/SBB: comprehensive coverage. amd64 section (11 funcs via `__int128`), i386 section (48 funcs via pure C + asm goto). Covers CondZ/NZ/S/B/O/NO for ADC; CondZ/B/NB/BE/NBE/L/NL/S/O/NO for SBB at 8,16,32 widths. |
| `ccop_copy.c` | 4 | COPY: CondZ/NZ via `pushf`/`popf` + `asm goto`. amd64 uses `pushfq`/`popfq` (64-bit), i386 uses `pushfl`/`popfl` (32-bit). |
| `ccop_rflags_c.c` | 6 | rflags_c: carry flag extraction for ADD (32,64), SUB (32,64), DEC (32,64). amd64-only (`#ifdef __x86_64__`). Uses `adc $0` / `sbb $0` carry chains. |

### Python Test Harness

`tests/analyses/decompiler/test_ccop_triggers.py` — parametrized pytest (54 test cases)
that decompiles every `ccop_*` function in every binary and asserts:

1. Decompilation succeeds (codegen is not None)
2. For rewriter-handled (op, cond, width) combos: no raw `calculate_condition` or
`calculate_rflags_c` in the output
3. For fallback-only combos (no rewriter): just checks decompilation doesn't crash

Run: `python -m pytest tests/analyses/decompiler/test_ccop_triggers.py -v`

---

## Comprehensive Rewriter Coverage vs Test Coverage

The tables below map every (ccop, condition) combination handled by the rewriters against
what the test suite covers. This was generated by reading every code path in
`amd64_ccalls.py` and `x86_ccalls.py`.

### Legend

- **A** = amd64 rewriter handles this (B/W/L/Q widths)
- **X** = x86 rewriter handles this (B/W/L widths)
- All listed conditions now have full width coverage

### SUB — `ccop_sub.c` (48 functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondL | A, X | 8, 16, 32, 64 |
| CondNL | A, X | 8, 16, 32, 64 |
| CondLE | A, X | 8, 16, 32, 64 |
| CondNLE | A, X | 8, 16, 32, 64 |
| CondB | A, X | 8, 16, 32, 64 |
| CondNB | A, X | 8, 16, 32, 64 |
| CondBE | A, X | 8, 16, 32, 64 |
| CondNBE | A, X | 8, 16, 32, 64 |
| CondS | A, X | 8, 16, 32, 64 |
| CondNS | A, X | 8, 16, 32, 64 |

### ADD — `ccop_add.c` (39 functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondS | A, X | 8, 16, 32, 64 |
| CondNS | A, X | 8, 16, 32, 64 |
| CondO | A, X | 8, 16, 32, 64 |
| CondNO | A, X | 8, 16, 32, 64 |
| CondB | A, X | 8, 16, 32, 64 |
| CondLE | X only | 8, 16, 32, 64 |
| CondNLE | X only | 8, 16, 32, 64 |
| CondBE | X only | 8, 16, 32 |

### LOGIC — `ccop_logic.c` (38 functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondS | A, X | 8, 16, 32, 64 |
| CondNS | A, X | 8, 16, 32, 64 |
| CondL | A, X | 8, 16, 32, 64 |
| CondNL | A, X | 8, 16, 32, 64 |
| CondLE | A, X | 8, 16, 32, 64 |
| CondNLE | A, X | 8, 16, 32, 64 |
| CondB | X only | 8, 16, 32 |
| CondBE | X only | 8, 16, 32 |

### INC — `ccop_inc_dec.c` (24 INC functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondS | A | 8, 16, 32, 64 |
| CondNS | A | 8, 16, 32, 64 |
| CondO | X only | 8, 16, 32 |
| CondNO | X only | 8, 16, 32 |

Note: INC CondLE/CondNLE (32) are in the test but neither rewriter handles this — those
test fallback behavior only.

### DEC — `ccop_inc_dec.c` (24 DEC functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondS | A | 8, 16, 32, 64 |
| CondNS | A | 8, 16, 32, 64 |
| CondLE | A, X | 8, 16, 32, 64 |
| CondNLE | A, X | 8, 16, 32, 64 |

### SHL — `ccop_shl_shr.c` (16 SHL functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondS | A, X | 8, 16, 32, 64 |
| CondNS | A, X | 8, 16, 32, 64 |

### SHR — `ccop_shl_shr.c` (16 SHR functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 8, 16, 32, 64 |
| CondNZ | A, X | 8, 16, 32, 64 |
| CondS | A, X | 8, 16, 32, 64 |
| CondNS | A, X | 8, 16, 32, 64 |

### UMUL — `ccop_umul_smul.c` (8 UMUL functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondO | A, X | 8, 16, 32, 64 |
| CondNO | A, X | 8, 16, 32, 64 |

### SMUL — `ccop_umul_smul.c` (8 SMUL functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondO | A (B/W/L/Q), X (L only) | 8, 16, 32, 64 |
| CondNO | A (B/W/L/Q), X (L only) | 8, 16, 32, 64 |

### ADC — `ccop_adc_sbb.c`

The AMD64 rewriter does NOT handle ADC at all. Only x86 handles ADC.

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondO | X only | 8, 16, 32 |
| CondNO | X only | 8, 16, 32 |
| CondB | X only | 8, 16, 32 |

ADC CondZ/NZ/S are tested but NOT handled by any rewriter — they exercise fallback only.

### SBB — `ccop_adc_sbb.c`

AMD64 rewriter only handles SBB+CondB. x86 handles more conditions.

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondB | A, X | 8, 16, 32, 64 |
| CondBE | X only | 8, 16, 32 |
| CondNB | X only | 8, 16, 32 |
| CondNBE | X only | 8, 16, 32 |
| CondL | X only | 8, 16, 32 |
| CondNL | X only | 8, 16, 32 |
| CondO | X only | 8, 16, 32 |
| CondNO | X only | 8, 16, 32 |

SBB CondZ/S are tested but NOT handled by any rewriter — they exercise fallback only.

### COPY — `ccop_copy.c` (4 functions)

| Condition | Rewriter | Tested Widths |
|-----------|----------|---------------|
| CondZ | A, X | 32, 64 |
| CondNZ | A, X | 32, 64 |

### `amd64g_calculate_rflags_c` — `ccop_rflags_c.c` (6 functions, amd64-only)

| CCOP | Rewriter | Tested Widths |
|------|----------|---------------|
| ADD | A only | 32, 64 |
| SUB | A only | 32, 64 |
| DEC | A only | 32, 64 |

---

## Remaining Gaps (P2 — fallback-only, no rewriter exists)

These ccop+condition combos are NOT handled by either rewriter. Tests would only verify
the decompiler doesn't crash (produces a raw `_ccall`). Not urgent.

| Gap | Notes |
|-----|-------|
| ADC + CondZ/NZ/S | Tested already, no rewriter path exists |
| SBB + CondZ/S | Tested already, no rewriter path exists |
| INC + CondLE/NLE | Tested already, no rewriter path exists |
| CondP/CondNP (parity) | Not in any rewriter |
| ROL/ROR | Op types defined but no rewriter code |
| ANDN/BLSI/BLSMSK/BLSR | AMD64 BMI ops, no rewriter |
| ADCX/ADOX | AMD64 ADX ops, no rewriter |

---

## Build System

```
bash build.sh # builds 54 binaries (27 amd64, 27 i386)
```

Produces 3 optimization variants per source file x 2 arches:
- Standard sources: `-O1`, `-O2`, `-Os`
- INC/DEC: `-O1 -mtune-ctrl=use_incdec`, `-O2 -mtune=haswell`, `-Os -mtune-ctrl=use_incdec`
- ADC/SBB: same as standard

All with base flags: `-fno-if-conversion -fno-if-conversion2 -fno-tree-loop-if-convert -g`

Requires `gcc` and `gcc-multilib` (both installed on this system).

## File Layout

```
ccop_triggers/
├── build.sh # Build script
├── ccop_common.h # NOINLINE macro, volatile g_sink, sized typedefs
├── ccop_sub.c # 48 funcs — SUB/CMP conditions
├── ccop_add.c # 39 funcs — ADD conditions
├── ccop_logic.c # 38 funcs — LOGIC (test/and) conditions
├── ccop_inc_dec.c # 48 funcs — INC/DEC conditions (inline asm)
├── ccop_shl_shr.c # 32 funcs — SHL/SHR conditions
├── ccop_umul_smul.c # 16 funcs — multiply overflow
├── ccop_adc_sbb.c # ~59 funcs — ADC/SBB multi-precision (inline asm)
├── ccop_copy.c # 4 funcs — COPY (pushf/popf)
├── ccop_rflags_c.c # 6 funcs — rflags_c carry extraction (amd64)
├── .gitignore # Ignores bin/
├── STATUS.md # This file
└── bin/ # Compiled output (gitignored)
├── amd64/ # 27 binaries
└── i386/ # 27 binaries

../test_ccop_triggers.py # Pytest harness (54 parametrized test cases)
```
113 changes: 113 additions & 0 deletions tests_src/ccop_triggers/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/bin/bash
#
# Build all ccop trigger binaries for amd64 and i386.
# Usage: cd tests/analyses/decompiler/ccop_triggers && bash build.sh
#
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage hint points to tests/analyses/decompiler/ccop_triggers, but this script lives under tests_src/ccop_triggers. This is likely to mislead anyone trying to rebuild the binaries; update the usage line (and any other path references) to match the actual repo layout.

Copilot uses AI. Check for mistakes.
# Produces multiple variants per source file using different optimization
# levels, tuning targets, and feature flags to maximize the variety of
# ccop patterns the compiler emits.
#
# Requires: gcc, gcc-multilib (for -m32 builds)
#
set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"

# Base flags: suppress conditional moves so branches stay as branches,
# keep debug info for decompiler friendliness
BASE="-fno-if-conversion -fno-if-conversion2 -fno-tree-loop-if-convert -g"

# ─── Standard sources: work fine with default tuning ──────────────
STD_SRCS=(
ccop_sub
ccop_add
ccop_logic
ccop_shl_shr
ccop_umul_smul
ccop_copy
ccop_rflags_c
)

# ─── INC/DEC source: needs -mtune-ctrl=use_incdec to emit inc/dec ─
INCDEC_SRCS=(
ccop_inc_dec
)

# ─── ADC/SBB source: needs wide types (__int128 on amd64, uint64_t
# on i386). Standard flags are fine; the code itself triggers it.
ADCSBB_SRCS=(
ccop_adc_sbb
)

# Standard optimization variants
STD_VARIANTS=(
"O1::-O1"
"O2::-O2"
"Os::-Os"
)

# INC/DEC variants: force inc/dec emission via tuning control
# -mtune-ctrl=use_incdec: explicit GCC override (works with any -mtune)
# -mtune=haswell: Haswell naturally enables inc/dec
INCDEC_VARIANTS=(
"O1_incdec::-O1 -mtune-ctrl=use_incdec"
"O2_haswell::-O2 -mtune=haswell"
"Os_incdec::-Os -mtune-ctrl=use_incdec"
)

mkdir -p bin/amd64 bin/i386

FAIL=0
COUNT=0

build_one() {
local src="$1" name="$2" arch_flag="$3" extra_flags="$4" outdir="$5"
if gcc $arch_flag $BASE $extra_flags -o "${outdir}/${name}" "${src}.c" 2>&1; then
COUNT=$((COUNT + 1))
else
echo " FAILED: $name ($outdir)"
FAIL=1
fi
}

# Build standard sources with standard variants
for src in "${STD_SRCS[@]}"; do
for variant in "${STD_VARIANTS[@]}"; do
IFS=':' read -r tag _ flags <<< "$variant"
name="${src}_${tag}"
echo "Building $name ..."
build_one "$src" "$name" "" "$flags" "bin/amd64"
build_one "$src" "$name" "-m32" "$flags" "bin/i386"
done
done

# Build INC/DEC sources with inc/dec-forcing variants
for src in "${INCDEC_SRCS[@]}"; do
for variant in "${INCDEC_VARIANTS[@]}"; do
IFS=':' read -r tag _ flags <<< "$variant"
name="${src}_${tag}"
echo "Building $name ..."
build_one "$src" "$name" "" "$flags" "bin/amd64"
build_one "$src" "$name" "-m32" "$flags" "bin/i386"
done
done

# Build ADC/SBB sources with standard variants
for src in "${ADCSBB_SRCS[@]}"; do
for variant in "${STD_VARIANTS[@]}"; do
IFS=':' read -r tag _ flags <<< "$variant"
name="${src}_${tag}"
echo "Building $name ..."
build_one "$src" "$name" "" "$flags" "bin/amd64"
build_one "$src" "$name" "-m32" "$flags" "bin/i386"
done
done

echo ""
if [ $FAIL -eq 0 ]; then
echo "Done. Built $COUNT binaries in bin/amd64/ and bin/i386/"
else
echo "Done with errors ($COUNT succeeded). Check output above."
exit 1
fi
Loading