Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix USE_DS for MIPS ##emu #23307

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
146 changes: 121 additions & 25 deletions libr/arch/p/mips/plugin_cs.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,25 @@ R_IPI int mips_assemble(const char *str, ut64 pc, ut8 *out);


// call with delay slot
#define ES_CALL_DR(ra, addr) "pc,4,+,"ra",=,"ES_J(addr)
#define ES_CALL_DR(ra, addr) "pc,4,+,"ra",=,"ES_J_D(addr)
#define ES_CALL_D(addr) ES_CALL_DR("ra", addr)

// call without delay slot
#define ES_CALL_NDR(ra, addr) "pc,"ra",=,"ES_J(addr)
#define ES_CALL_NDR(ra, addr) "pc,"ra",=,"ES_J_ND(addr)
#define ES_CALL_ND(addr) ES_CALL_NDR("ra", addr)

#define USE_DS 0
#define USE_DS 1
#if USE_DS
// emit ERR trap if executed in a delay slot
#define ES_TRAP_DS() "$ds,!,!,?{,$$,1,TRAP,BREAK,},"
// jump to address
#define ES_J(addr) addr",SETJT,1,SETD"
// Record jump-to-address and set delay slot flag.
#define ES_J_D(addr) addr",SETJT,1,SETD"
// Jump to address.
#define ES_J_ND(addr) addr",pc,:="
#else
#define ES_TRAP_DS() ""
#define ES_J(addr) addr",pc,:="
#define ES_J_D(addr) addr",pc,:="
#define ES_J_ND(addr) ES_J_D(addr)
#endif

#define ES_B(x) "0xff,"x",&"
Expand Down Expand Up @@ -241,6 +244,7 @@ static const char *arg(csh *handle, cs_insn *insn, char *buf, size_t buf_sz, int
static int analop_esil(RArchSession *as, RAnalOp *op, csh *handle, cs_insn *insn) {
char str[8][32] = {{0}};
int i;
u_int64_t addr = insn->address;
Copy link
Collaborator

Choose a reason for hiding this comment

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

we use ut64 in r2land, its an alias to uint64_t but just good for consistency sugar

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I also hate writing out u_int64_t ^^


r_strbuf_init (&op->esil);
r_strbuf_set (&op->esil, "");
Expand Down Expand Up @@ -314,6 +318,15 @@ static int analop_esil(RArchSession *as, RAnalOp *op, csh *handle, cs_insn *insn
case MIPS_INS_SLL:
r_strbuf_appendf (&op->esil, "%s,%s,<<,%s,=", ARG (2), ARG (1), ARG (0));
break;
case MIPS_INS_BALC:
// BALC address
// Branch And Link, Compact. Unconditional PC relative branch to address, placing return address
// in register $31.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "" ES_CALL_ND ("%s"), ARG (0));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

$$ is a keyword that should be removed because we have PC and op->addr already, so there's no need to have an extra keyword for that. also its better to build the string right away instead of using replacef

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So would you suggest to change the macro like this to avoid $$:

#if USE_DS
#define ES_TRAP_DS(addr) "$ds,!,!,?{," addr ",1,TRAP,BREAK,},"
#else
#define ES_TRAP_DS(addr) ""
#endif

With that we can remove the #if USE_DS -> r_strbuf_replacef stuff and build the string in-place by rewriting the r_strbuf_appendf call to:

#if USE_DS
    r_strbuf_appendf (&op->esil, ES_TRAP_DS ("0x%"PFMT64x) "" ES_CALL_DR ("%s", "%s"), addr, ARG (0), ARG (1));
#else
    r_strbuf_appendf (&op->esil, "" ES_CALL_DR ("%s", "%s"), ARG (0), ARG (1));
#endif

where addr is op->addr or insn->address (I guess that's the same). The downside I see is that by unconditionally passing addr to the invocation we must duplicate the code. Alternatively, one could to a bad hack like:

r_strbuf_appendf (&op->esil, ES_TRAP_DS ("0x%3$"PFMT64x) "" ES_CALL_DR ("%s", "%s"), ARG (0), ARG (1), addr);

But that could, depending on compiler settings, complain about the wrong number of args in the format string.

The problem with using PC is that AFAIK it points to the first byte after instruction, and we would need to subtract its size, which may actually be 4 or 2 bytes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from what i get here USE_DS makes the expressions more complex to trap when an instruction is executed if the delay slot is going on and the instruction cant happen there. i assume this can only happen on invalid genereated code by compilers or linearly emulating the code.

FYI, the manual has the following to say about putting a branch/jump into the branch delay slot: "If a branch or jump instruction is placed in the branch delay slot, the operation of both instructions is undefined." So I guess what happens depends on the impl of the CPU u are running on. To me, it seems like trapping on that is reasonable, but given that behavior is undefined, it would also be OK to throw this part out completely and just let the emulator decide.

Copy link
Collaborator

Choose a reason for hiding this comment

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

what mainly worries me here is the fact that there are two compile time ways to have this esil expression. so i would prefer to have only one and not depend on compile time options

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, so completely throw away the USE_DS define and always emit the version with the trap code and SETD/SETJT directives?

Copy link
Collaborator

Choose a reason for hiding this comment

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

well. if the code is trapping, the linear emulation code should be ignoring those stop points.. did you had a look at the failing tests?

Copy link
Collaborator

Choose a reason for hiding this comment

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

i mean in theory the change looks good to me, but will need some refinements and checks to see if the behaviour keeps correct for the current usecases. thanks for taking the time!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the delay - was on vacation. Then I'll now work on a version with only one compile time version of the ESIL. Once that one is ready, I'll certainly need some help since I have no idea where those changes might break something (besides the tests of course) in the larger r2 picture.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Count on me for that help, don't hesitate to make questions or join discord/telegram/matrix channel for a more fluent chat. Thanks!

#endif
break;
case MIPS_INS_BAL:
case MIPS_INS_JAL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "" ES_CALL_D ("%s"), ARG (0));
Expand Down Expand Up @@ -348,48 +361,94 @@ static int analop_esil(RArchSession *as, RAnalOp *op, csh *handle, cs_insn *insn
break;
case MIPS_INS_JRADDIUSP:
// increment stackpointer in X and jump to %ra
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,sp,+=," ES_J ("ra"), ARG (0));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,sp,+=," ES_J_D ("ra"), ARG (0));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_JR:
case MIPS_INS_JRC:
case MIPS_INS_BC:
// JRC rt
// Jump Register, Compact. Unconditional jump to address in register $rt.
// BC address
// Branch, Compact. Unconditional PC relative branch to address.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "" ES_J_ND ("%s"), ARG (0));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_JR:
case MIPS_INS_J:
case MIPS_INS_B: // ???
// jump to address with conditional
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "" ES_J ("%s"), ARG (0));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "" ES_J_D ("%s"), ARG (0));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BNEC:
// BNEC rs, rt, address
// Branch Not Equal, Compact. PC relative branch to address if register $rs is not equal to
// register $rt.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,==,$z,!,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1), ARG (2));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BNE: // bne $s, $t, offset
case MIPS_INS_BNEL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,==,$z,!,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,==,$z,!,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1), ARG (2));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BEQC:
// BEQC rs, rt, address
// Branch if Equal, Compact. PC relative branch to address if registers $rs and $rt are are equal.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,==,$z,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1), ARG (2));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BEQ:
case MIPS_INS_BEQL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,==,$z,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,==,$z,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1), ARG (2));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BEQZC:
// BEQZC rt, address # when rt and address are in range
// Branch if Equal to Zero, Compact. PC relative branch to address if register $rt equals zero.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,0,==,$z,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BZ:
case MIPS_INS_BEQZ:
case MIPS_INS_BEQZC:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,0,==,$z,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,0,==,$z,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BNEZC:
// BNEZC rt, address
// Branch if Not Equal to Zero, Compact. PC relative branch to address if register $rt is not equal to zero.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,0,==,$z,!,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BNEZ:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,0,==,$z,!,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,0,==,$z,!,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
Expand All @@ -402,21 +461,45 @@ static int analop_esil(RArchSession *as, RAnalOp *op, csh *handle, cs_insn *insn
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BLEZ:
case MIPS_INS_BLEZC:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,%s,==,$z,?{," ES_J_ND ("%s") ",BREAK,},",
ARG (0), ARG (1));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "1," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BLEZ:
case MIPS_INS_BLEZL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,%s,==,$z,?{," ES_J ("%s") ",BREAK,},",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,%s,==,$z,?{," ES_J_D ("%s") ",BREAK,},",
ARG (0), ARG (1));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "1," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "1," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BGEZ:
case MIPS_INS_BGEC:
// BGEC rs, rt, address
// Branch if Greater than or Equal, Compact. PC relative branch to address if register $rs
// is greater than or equal to register $rt.
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "%s,%s,>=,$z,?{," ES_J_ND ("%s") ",}",
ARG (1), ARG (0), ARG (2));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BGEZC:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BGEZ:
case MIPS_INS_BGEZL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
Expand Down Expand Up @@ -450,33 +533,46 @@ static int analop_esil(RArchSession *as, RAnalOp *op, csh *handle, cs_insn *insn
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BLTZ:
case MIPS_INS_BLTZC:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "1," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BLTZ:
case MIPS_INS_BLTZL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "1," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "1," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BGTZ:
case MIPS_INS_BGTZC:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,%s,==,$z,?{,BREAK,},", ARG (0));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_ND ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BGTZ:
case MIPS_INS_BGTZL:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,%s,==,$z,?{,BREAK,},", ARG (0));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J ("%s") ",}",
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0," ES_IS_NEGATIVE ("%s") ",==,$z,?{," ES_J_D ("%s") ",}",
ARG (0), ARG (1));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BTEQZ:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,t,==,$z,?{," ES_J ("%s") ",}", ARG (0));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,t,==,$z,?{," ES_J_D ("%s") ",}", ARG (0));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
break;
case MIPS_INS_BTNEZ:
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,t,==,$z,!,?{," ES_J ("%s") ",}", ARG (0));
r_strbuf_appendf (&op->esil, ES_TRAP_DS () "0,t,==,$z,!,?{," ES_J_D ("%s") ",}", ARG (0));
#if USE_DS
r_strbuf_replacef (&op->esil, "$$", "0x%"PFMT64x, addr);
#endif
Expand Down
Loading
Loading