Repository: capstone-engine/capstone (2025-06-25T08:07:11Z)

Issue:
s390x incorrect disassembly `exrl` inst since capstone 6.0.0a4
found during: https://github.com/pwndbg/pwndbg/issues/3088

Capstone6:
```
[nix-shell:/persist/capstone]# ./build/cstool -version
cstool for Capstone Disassembler, v6.0.0
Capstone build: x86=1 arm=1 aarch64=1 mips=1 ppc=1 sparc=1 systemz=1 xcore=1 m68k=1 tms320c64x=1 m680x=1 evm=1 wasm=1 mos65xx=1 bpf=1 riscv=1 sh=1 tricore=1 alpha=1 hppa=1 loongarch=1 xtensa=1 arc=1

[nix-shell:/persist/capstone]# ./build/cstool -d s390x c60000000005
 0  c6 00 00 00 00 05  exrl     0, 0xa
        ID: 740 (exrl)
        op_count: 2
Hit assert: RegNo && RegNo < 195 && "Invalid register number!"
                operands[0].type: REG = (null)
                operands[0].access: READ
                operands[1].type: IMM = 0xa
                operands[1].access: READ
```

capstone5:
```
[nix-shell:/persist/capstone]# cstool -version
cstool for Capstone Disassembler, v5.0.3
Capstone build: x86=1 arm=1 arm64=1 mips=1 ppc=1 sparc=1 sysz=1 xcore=1 m68k=1 tms320c64x=1 m680x=1 evm=1 wasm=1 mos65xx=1 bpf=1 riscv=1 sh=1 tricore=1

[nix-shell:/persist/capstone]# cstool -d s390x c60000000005
 0  c6 00 00 00 00 05  exrl     %r0, 0xa
        ID: 1157 (exrl)
        op_count: 2
                operands[0].type: REG = 0
                operands[1].type: IMM = 0xa
```


Discussion:
Possibly related to https://github.com/llvm/llvm-project/issues/44437
This should be `r0` I guess?
Turns out they just never implemented it in the disassembler.
See
https://github.com/capstone-engine/capstone/blob/a25d4980b02d506f25d43c3f83f0c93f0973c722/arch/SystemZ/SystemZDisassembler.c#L82-L84

@patryk4815 Is r0 an illegal register to use? I struggle to make sense of the ISA.
`r0` should be legal to use. For example it is used here: https://elixir.bootlin.com/linux/v6.15.2/source/arch/s390/boot/head.S

I'll fix it in Capstone, but for reference: https://github.com/llvm/llvm-project/issues/144869
Oh boy, this breaks a ton of tests. I'll wait a few days to see what the LLVM people say about it. They might know some reason it is like this.
Turns out this [is expected](https://github.com/llvm/llvm-project/issues/144869#issuecomment-2994325375). I think for Capstone we should just replace it with an immediate of 0 then? wdyt?
> Turns out this [is expected](https://github.com/llvm/llvm-project/issues/144869#issuecomment-2994325375). I think for Capstone we should just replace it with an immediate of 0 then? wdyt?

I think printing 0 would be best
Yeah, plan was to fix it tomorrow like this.
Btw when can we expect a Capstone release with this fix being included?:)

(We are waiting for it in [Pwndbg](https://github.com/pwndbg/pwndbg/issues/3088#issuecomment-3133344331))

The next release was planned to be `Beta1`. End of fall (October/November).
We could make one in between.
Because for the Beta release we still have one or two things to add.
Code formatting, fixing 32bit builds, RISCV update, fix the DIET build.
Formatting and RISCV are in the making.
But I haven't found time for 32bit and DIET. But DIET is a pretty big one. Possibly we can move it to the Beta phase as well.

I'll ask the others if we can another Alpha release (with new Sparc).



Patch:
diff --git a/arch/SystemZ/SystemZMapping.c b/arch/SystemZ/SystemZMapping.c
index fd90c65d36..a677436afe 100644
--- a/arch/SystemZ/SystemZMapping.c
+++ b/arch/SystemZ/SystemZMapping.c
@@ -321,6 +321,21 @@ void SystemZ_set_detail_op_reg(MCInst *MI, unsigned op_num, systemz_reg Reg)
 	if (!detail_is_set(MI))
 		return;
 	CS_ASSERT((map_get_op_type(MI, op_num) & ~CS_OP_MEM) == CS_OP_REG);
+	if (Reg == SYSTEMZ_REG_INVALID) {
+		// This case is legal. The ISA says:
+		// "
+		// When the R1 field is not zero, bits 8-15 of the instruction designated
+		// by the second-operand address are ORed with bits 56-63 of
+		// general register R1. [...] When the R1 field is zero, no ORing takes place
+		// "
+		// This means we just save the neutral element for ORing, so 0.
+		SystemZ_get_detail_op(MI, 0)->type = SYSTEMZ_OP_IMM;
+		SystemZ_get_detail_op(MI, 0)->imm = 0;
+		SystemZ_get_detail_op(MI, 0)->access = map_get_op_access(MI, op_num);
+		SystemZ_get_detail_op(MI, 0)->imm_width = 0;
+		SystemZ_inc_op_count(MI);
+		return;
+	}
 
 	SystemZ_get_detail_op(MI, 0)->type = SYSTEMZ_OP_REG;
 	SystemZ_get_detail_op(MI, 0)->reg = Reg;


Test patch:
diff --git a/tests/issues/issues.yaml b/tests/issues/issues.yaml
index 749cdac918..f27bbe34da 100644
--- a/tests/issues/issues.yaml
+++ b/tests/issues/issues.yaml
@@ -6262,4 +6262,22 @@ test_cases:
     expected:
       insns:
         -
-          asm_text: "c.srli a4, 5"
\ No newline at end of file
+          asm_text: "c.srli a4, 5"
+  -
+    input:
+      name: "issue 2741 - SystemZ addresses with 0 operand."
+      bytes: [ 0xc6,0x00,0x00,0x00,0x00,0x05 ]
+      arch: "CS_ARCH_SYSTEMZ"
+      options: [ CS_OPT_DETAIL, CS_MODE_BIG_ENDIAN ]
+      address: 0x0
+    expected:
+      insns:
+      -
+        asm_text: "exrl	0, 0xa"
+        details:
+          systemz:
+            operands:
+              - type: SYSTEMZ_OP_IMM
+                imm: 0
+              - type: SYSTEMZ_OP_IMM
+                imm: 0xa