Skip to content

Commit

Permalink
target/arm: Fix crash on conditional instruction in an IT block
Browse files Browse the repository at this point in the history
If an instruction is conditional (like CBZ) and it is executed
conditionally (using the ITx instruction), a jump to an undefined
label is generated, and QEMU crashes.

CBZ in IT block is an UNPREDICTABLE behavior, but we should not
crash.  Honouring the condition code is allowed by the spec in this
case (constrained unpredictable, ARMv8, section K1.1.7), and matches
what we do for other "UNPREDICTABLE inside an IT block" instructions.

Fix the 'skip on condition' code to create a new label only if it
does not already exist.  Previously multiple labels were created, but
only the last one of them was set.

Signed-off-by: Roman Kapl <rka@sysgo.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20180816120533.6587-1-rka@sysgo.com
[PMM: fixed ^ 1 being applied to wrong argument, fixed typo]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
  • Loading branch information
Roman Kapl authored and pm215 committed Aug 20, 2018
1 parent adaec19 commit c2d9644
Showing 1 changed file with 21 additions and 14 deletions.
35 changes: 21 additions & 14 deletions target/arm/translate.c
Expand Up @@ -8480,6 +8480,22 @@ static void gen_srs(DisasContext *s,
s->base.is_jmp = DISAS_UPDATE;
}

/* Generate a label used for skipping this instruction */
static void arm_gen_condlabel(DisasContext *s)
{
if (!s->condjmp) {
s->condlabel = gen_new_label();
s->condjmp = 1;
}
}

/* Skip this instruction if the ARM condition is false */
static void arm_skip_unless(DisasContext *s, uint32_t cond)
{
arm_gen_condlabel(s);
arm_gen_test_cc(cond ^ 1, s->condlabel);
}

static void disas_arm_insn(DisasContext *s, unsigned int insn)
{
unsigned int cond, val, op1, i, shift, rm, rs, rn, rd, sh;
Expand Down Expand Up @@ -8709,9 +8725,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
if (cond != 0xe) {
/* if not always execute, we generate a conditional jump to
next instruction */
s->condlabel = gen_new_label();
arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
arm_skip_unless(s, cond);
}
if ((insn & 0x0f900000) == 0x03000000) {
if ((insn & (1 << 21)) == 0) {
Expand Down Expand Up @@ -11205,9 +11219,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
/* Conditional branch. */
op = (insn >> 22) & 0xf;
/* Generate a conditional jump to next instruction. */
s->condlabel = gen_new_label();
arm_gen_test_cc(op ^ 1, s->condlabel);
s->condjmp = 1;
arm_skip_unless(s, op);

/* offset[11:1] = insn[10:0] */
offset = (insn & 0x7ff) << 1;
Expand Down Expand Up @@ -12131,8 +12143,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
case 1: case 3: case 9: case 11: /* czb */
rm = insn & 7;
tmp = load_reg(s, rm);
s->condlabel = gen_new_label();
s->condjmp = 1;
arm_gen_condlabel(s);
if (insn & (1 << 11))
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel);
else
Expand Down Expand Up @@ -12295,9 +12306,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
break;
}
/* generate a conditional jump to next instruction */
s->condlabel = gen_new_label();
arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
arm_skip_unless(s, cond);

/* jump to the offset */
val = (uint32_t)s->pc + 2;
Expand Down Expand Up @@ -12676,9 +12685,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
uint32_t cond = dc->condexec_cond;

if (cond != 0x0e) { /* Skip conditional when condition is AL. */
dc->condlabel = gen_new_label();
arm_gen_test_cc(cond ^ 1, dc->condlabel);
dc->condjmp = 1;
arm_skip_unless(dc, cond);
}
}

Expand Down

0 comments on commit c2d9644

Please sign in to comment.