Skip to content

Commit

Permalink
Improve ARM64 relocator to support b and bl
Browse files Browse the repository at this point in the history
  • Loading branch information
Ole André Vadla Ravnås committed Apr 21, 2014
1 parent f85008c commit 0294de8
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 1 deletion.
2 changes: 2 additions & 0 deletions gum/arch-arm64/gumarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ enum _GumArm64Mnemonic

GUM_ARM64_ADR,
GUM_ARM64_ADRP,
GUM_ARM64_B,
GUM_ARM64_BL,
GUM_ARM64_BR,
GUM_ARM64_BLR,
GUM_ARM64_RET
Expand Down
57 changes: 56 additions & 1 deletion gum/arch-arm64/gumarm64relocator.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct _GumCodeGenCtx

static gboolean gum_arm64_relocator_rewrite_adr (GumArm64Relocator * self,
GumCodeGenCtx * ctx);
static gboolean gum_arm64_relocator_rewrite_bl (GumArm64Relocator * self,
GumCodeGenCtx * ctx);

void
gum_arm64_relocator_init (GumArm64Relocator * relocator,
Expand Down Expand Up @@ -119,7 +121,22 @@ gum_arm64_relocator_read_one (GumArm64Relocator * self,
insn->length = 4;
insn->pc = self->input_pc;

if ((raw_insn & 0xff9ffc1f) == 0xd61f0000)
if ((raw_insn & 0x7c000000) == 0x14000000)
{
if ((raw_insn & 0x80000000) != 0)
{
insn->mnemonic = GUM_ARM64_BL;
self->eob = TRUE;
self->eoi = FALSE;
}
else
{
insn->mnemonic = GUM_ARM64_B;
self->eob = TRUE;
self->eoi = TRUE;
}
}
else if ((raw_insn & 0xff9ffc1f) == 0xd61f0000)
{
switch ((raw_insn >> 21) & 3)
{
Expand Down Expand Up @@ -223,6 +240,10 @@ gum_arm64_relocator_write_one (GumArm64Relocator * self)
case GUM_ARM64_ADRP:
rewritten = gum_arm64_relocator_rewrite_adr (self, &ctx);
break;
case GUM_ARM64_B:
case GUM_ARM64_BL:
rewritten = gum_arm64_relocator_rewrite_bl (self, &ctx);
break;
default:
break;
}
Expand Down Expand Up @@ -364,3 +385,37 @@ gum_arm64_relocator_rewrite_adr (GumArm64Relocator * self,

return TRUE;
}

static gboolean
gum_arm64_relocator_rewrite_bl (GumArm64Relocator * self,
GumCodeGenCtx * ctx)
{
union
{
gint32 i;
guint32 u;
} distance;
GumAddress absolute_target;

if ((ctx->raw_insn & 0x2000000) != 0)
distance.u = 0xfc000000 | (ctx->raw_insn & 0x3ffffff);
else
distance.u = ctx->raw_insn & 0x3ffffff;

absolute_target = ctx->insn->pc + (distance.i * 4);

if (ctx->insn->mnemonic == GUM_ARM64_B)
{
gum_arm64_writer_put_ldr_reg_address (ctx->output, GUM_A64REG_X16,
absolute_target);
gum_arm64_writer_put_br_reg (ctx->output, GUM_A64REG_X16);
}
else
{
gum_arm64_writer_put_ldr_reg_address (ctx->output, GUM_A64REG_LR,
absolute_target);
gum_arm64_writer_put_blr_reg (ctx->output, GUM_A64REG_LR);
}

return TRUE;
}
77 changes: 77 additions & 0 deletions tests/core/arch-arm64/arm64relocator.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ TEST_LIST_BEGIN (arm64relocator)
TESTENTRY (one_to_one)
TESTENTRY (adr_should_be_rewritten)
TESTENTRY (adrp_should_be_rewritten)
TESTENTRY (b_should_be_rewritten)
TESTENTRY (bl_should_be_rewritten)
TESTENTRY (eob_and_eoi_on_ret)
TEST_LIST_END ()

Expand Down Expand Up @@ -113,6 +115,81 @@ TESTCASE (adrp_should_be_rewritten)
sizeof (expected_output)), ==, 0);
}

typedef struct _BranchScenario BranchScenario;

struct _BranchScenario
{
GumArm64Mnemonic mnemonic;
guint32 input[1];
gsize input_length;
guint32 expected_output[4];
gsize expected_output_length;
gsize pc_offset;
gssize expected_pc_distance;
};

static void branch_scenario_execute (BranchScenario * bs,
TestArm64RelocatorFixture * fixture);

TESTCASE (b_should_be_rewritten)
{
BranchScenario bs = {
GUM_ARM64_B,
{ 0x17ffff5a }, 1, /* b #-664 */
{
0x58000050, /* ldr x16, [pc, #8] */
0xd61f0200, /* br x16 */
0xffffffff, /* <calculated PC */
0xffffffff /* goes here> */
}, 4,
2, -664
};
branch_scenario_execute (&bs, fixture);
}

TESTCASE (bl_should_be_rewritten)
{
BranchScenario bs = {
GUM_ARM64_BL,
{ 0x97ffff5a }, 1, /* bl #-664 */
{
0x5800005e, /* ldr lr, [pc, #8] */
0xd63f03c0, /* blr lr */
0xffffffff, /* <calculated PC */
0xffffffff /* goes here> */
}, 4,
2, -664
};
branch_scenario_execute (&bs, fixture);
}

static void
branch_scenario_execute (BranchScenario * bs,
TestArm64RelocatorFixture * fixture)
{
gsize i;
guint32 calculated_pc;
const GumArm64Instruction * insn = NULL;

for (i = 0; i != bs->input_length; i++)
bs->input[i] = GUINT32_TO_LE (bs->input[i]);
for (i = 0; i != bs->expected_output_length; i++)
bs->expected_output[i] = GUINT32_TO_LE (bs->expected_output[i]);

SETUP_RELOCATOR_WITH (bs->input);

calculated_pc = fixture->rl.input_pc + bs->expected_pc_distance;
*((guint64 *) (bs->expected_output + bs->pc_offset)) =
GUINT64_TO_LE (calculated_pc);

g_assert_cmpuint (gum_arm64_relocator_read_one (&fixture->rl, &insn), ==, 4);
g_assert_cmpint (insn->mnemonic, ==, bs->mnemonic);
g_assert (gum_arm64_relocator_write_one (&fixture->rl));
gum_arm64_writer_flush (&fixture->aw);
g_assert_cmpint (memcmp (fixture->output, bs->expected_output,
bs->expected_output_length * sizeof (guint32)), ==, 0);
}

TESTCASE (eob_and_eoi_on_ret)
{
const guint32 input[] = {
Expand Down

0 comments on commit 0294de8

Please sign in to comment.