Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CMakeLists.txt

@Rot127 Rot127 Jul 24, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

in order to run this test you need to add a step in CITest.yaml.
Its a little more annoying than on the next branch because here it was never refactored.
Just adding a simple new step below the cstest one should be enough.

Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ if(CAPSTONE_PPC_SUPPORT)
arch/PowerPC/PPCGenRegisterInfo.inc
arch/PowerPC/PPCGenInstrInfo.inc
)
set(TEST_SOURCES ${TEST_SOURCES} test_ppc.c)
set(TEST_SOURCES ${TEST_SOURCES} test_ppc.c test_ppc_iter_detail.c)
endif()

if(CAPSTONE_X86_SUPPORT)
Expand Down
14 changes: 8 additions & 6 deletions arch/PowerPC/PPCInstPrinter.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ void PPC_post_printer(csh ud, cs_insn *insn, char *insn_asm, MCInst *mci)
if (((cs_struct *)ud)->detail != CS_OPT_ON)
return;

// check if this insn has branch hint
if (strrchr(insn->mnemonic, '+') != NULL && !strstr(insn_asm, ".+")) {
// insn->mnemonic is not filled yet; the record-form '.' and the branch
// hint '+'/'-' are always the last char of the mnemonic token of insn_asm
size_t n = strcspn(insn_asm, " \t");
char c = n ? insn_asm[n - 1] : 0;

if (c == '+') {
insn->detail->ppc.bh = PPC_BH_PLUS;
} else if (strrchr(insn->mnemonic, '-') != NULL) {
} else if (c == '-') {
insn->detail->ppc.bh = PPC_BH_MINUS;
}

if (strrchr(insn->mnemonic, '.') != NULL) {
} else if (c == '.') {
insn->detail->ppc.update_cr0 = true;
}
}
Expand Down
3 changes: 3 additions & 0 deletions cs.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,9 @@ static void fill_insn(struct cs_struct *handle, cs_insn *insn, char *buffer, MCI
// we might skip some redundant bytes in front in the case of X86
memcpy(insn->bytes, code + insn->size - copy_size, copy_size);
insn->op_str[0] = '\0';
// mnemonic is filled below, after the post printer runs: post printers
// must derive any checks from insn_asm, never from insn->mnemonic
insn->mnemonic[0] = '\0';
insn->size = copy_size;

// alias instruction might have ID saved in OpcodePub
Expand Down
12 changes: 12 additions & 0 deletions suite/cstest/issues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,3 +1118,15 @@
!# MOVSXD r16, m32 (memory form, 0x66 prefix)
!# CS_ARCH_X86, CS_MODE_64, CS_OPT_DETAIL
0x0: 0x66, 0x63, 0x20 == movsxd sp, dword ptr [rax]

!# issue PPC record form sets Update-CR0
!# CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL
0x7c,0x85,0x32,0x15 == add. r4, r5, r6 ; Update-CR0: True

!# issue PPC branch hint plus
!# CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL
0x40,0xe2,0x00,0x10 == bne+ 0x10 ; Branch hint: 1

!# issue PPC branch hint minus
!# CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL
0x40,0xc2,0x00,0x10 == bne- 0x10 ; Branch hint: 2
1 change: 1 addition & 0 deletions tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ endif
ifneq (,$(findstring powerpc,$(CAPSTONE_ARCHS)))
CFLAGS += -DCAPSTONE_HAS_POWERPC
SOURCES += test_ppc.c
SOURCES += test_ppc_iter_detail.c
endif
ifneq (,$(findstring sparc,$(CAPSTONE_ARCHS)))
CFLAGS += -DCAPSTONE_HAS_SPARC
Expand Down
90 changes: 90 additions & 0 deletions tests/test_ppc_iter_detail.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* Capstone Disassembly Engine */
/* By phix33, 2026 */

// Regression test: update_cr0 and bh (branch hint) must be derived from the
// current instruction only. PPC_post_printer used to read insn->mnemonic,
// which the alias early-return paths in PPC_printInst (mr, slwi/srwi, sldi,
// dcbt/dcbtst, dcbf) never fill before it runs. When one cs_insn buffer is
// reused across cs_disasm_iter() calls - the documented usage pattern - those
// aliases inherited update_cr0/bh from the previously decoded instruction.

#include <stdio.h>

#include <capstone/platform.h>
#include <capstone/capstone.h>

struct step {
const char *code; // 4 bytes, big-endian
const char *asm_text;
bool update_cr0;
ppc_bh bh;
};

// Each alias is decoded right after an instruction that legitimately sets
// update_cr0 or a branch hint, into the same reused cs_insn.
static const struct step steps[] = {
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x29\x0b\x78", "mr r9, r1", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x54\xa4\x18\x38", "slwi r4, r5, 3", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x78\xa4\x1f\x24", "sldi r4, r5, 3", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x04\x2a\x2c", "dcbt r4, r5", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x04\x28\xac", "dcbf r4, r5", false, PPC_BH_INVALID },
// non-alias path after a record form: was already correct
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x85\x32\x14", "add r4, r5, r6", false, PPC_BH_INVALID },
// branch hints must not leak into the aliases either
{ "\x40\xe2\x00\x10", "bne+ 0x1010", false, PPC_BH_PLUS },
{ "\x7c\x29\x0b\x78", "mr r9, r1", false, PPC_BH_INVALID },
{ "\x40\xc2\x00\x10", "bne- 0x1010", false, PPC_BH_MINUS },
{ "\x7c\x04\x2a\x2c", "dcbt r4, r5", false, PPC_BH_INVALID },
};

int main(void)
{
csh handle;
cs_insn *insn;
int i, errors = 0;

if (cs_open(CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, &handle) != CS_ERR_OK) {
printf("ERROR: Failed to initialize engine!\n");
return 1;
}
cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);

insn = cs_malloc(handle);

for (i = 0; i < (int)(sizeof(steps) / sizeof(steps[0])); i++) {
const uint8_t *code = (const uint8_t *)steps[i].code;
size_t size = 4;
uint64_t address = 0x1000;

if (!cs_disasm_iter(handle, &code, &size, &address, insn)) {
printf("ERROR: failed to decode '%s'\n", steps[i].asm_text);
errors++;
continue;
}

bool ok = insn->detail->ppc.update_cr0 == steps[i].update_cr0 &&
insn->detail->ppc.bh == steps[i].bh;
printf("%s\t%s: update_cr0=%u (expected %u), bh=%u (expected %u)\n",
ok ? "OK " : "FAIL", steps[i].asm_text,
insn->detail->ppc.update_cr0, steps[i].update_cr0,
insn->detail->ppc.bh, steps[i].bh);
if (!ok)
errors++;
}

cs_free(insn, 1);
cs_close(&handle);

if (errors) {
printf("%d test(s) failed\n", errors);
return 1;
}
printf("all tests passed\n");
return 0;
}
Loading