Skip to content

Commit

Permalink
Improve ARM64 relocator to support adr and adrp
Browse files Browse the repository at this point in the history
  • Loading branch information
Ole André Vadla Ravnås committed Apr 15, 2014
1 parent 39eedc7 commit 3635815
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 23 deletions.
6 changes: 5 additions & 1 deletion gum/arch-arm64/gumarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ typedef struct _GumArm64Instruction GumArm64Instruction;

enum _GumArm64Mnemonic
{
GUM_ARM64_UNKNOWN
GUM_ARM64_UNKNOWN,

GUM_ARM64_ADR,
GUM_ARM64_ADRP
};

enum _GumArm64Reg
Expand Down Expand Up @@ -115,6 +118,7 @@ struct _GumArm64Instruction

gconstpointer address;
guint length;
GumAddress pc;
};

G_END_DECLS
Expand Down
106 changes: 85 additions & 21 deletions gum/arch-arm64/gumarm64relocator.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ typedef struct _GumCodeGenCtx GumCodeGenCtx;
struct _GumCodeGenCtx
{
const GumArm64Instruction * insn;
const guint32 * raw_insn;
guint32 raw_insn;
const guint8 * start;
const guint8 * end;
guint len;

GumArm64Writer * output;
};

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

void
gum_arm64_relocator_init (GumArm64Relocator * relocator,
Expand All @@ -57,10 +57,13 @@ gum_arm64_relocator_reset (GumArm64Relocator * relocator,
gconstpointer input_code,
GumArm64Writer * output)
{
relocator->input_start = relocator->input_cur = input_code;
relocator->input_start = input_code;
relocator->input_cur = input_code;
relocator->input_pc = GUM_ADDRESS (input_code);
relocator->output = output;

relocator->inpos = relocator->outpos = 0;
relocator->inpos = 0;
relocator->outpos = 0;

relocator->eob = FALSE;
relocator->eoi = FALSE;
Expand Down Expand Up @@ -100,27 +103,36 @@ gum_arm64_relocator_increment_outpos (GumArm64Relocator * self)

guint
gum_arm64_relocator_read_one (GumArm64Relocator * self,
const GumArm64Instruction ** instruction)
const GumArm64Instruction ** instruction)
{
guint32 raw_insn;
GumArm64Instruction * insn;
guint32 adr_bits;

if (self->eoi)
return 0;

raw_insn = *((guint32 *) self->input_cur);
raw_insn = GUINT32_FROM_LE (*((guint32 *) self->input_cur));
insn = &self->input_insns[gum_arm64_relocator_inpos (self)];

insn->mnemonic = GUM_ARM64_UNKNOWN;
insn->length = 4;
insn->address = self->input_cur;
insn->length = 4;
insn->pc = self->input_pc;

adr_bits = raw_insn & 0x9f000000;
if (adr_bits == 0x10000000)
insn->mnemonic = GUM_ARM64_ADR;
else if (adr_bits == 0x90000000)
insn->mnemonic = GUM_ARM64_ADRP;

gum_arm64_relocator_increment_inpos (self);

if (instruction != NULL)
*instruction = insn;

self->input_cur += insn->length;
self->input_pc += insn->length;

return self->input_cur - self->input_start;
}
Expand Down Expand Up @@ -161,30 +173,33 @@ gboolean
gum_arm64_relocator_write_one (GumArm64Relocator * self)
{
GumArm64Instruction * cur;
GumCodeGenCtx ctx;
gboolean rewritten = FALSE;

if ((cur = gum_arm64_relocator_peek_next_write_insn (self)) == NULL)
return FALSE;

return gum_arm64_relocator_write_one_instruction (self);
}

static gboolean
gum_arm64_relocator_write_one_instruction (GumArm64Relocator * self)
{
GumCodeGenCtx ctx;
gboolean rewritten = FALSE;

if ((ctx.insn = gum_arm64_relocator_peek_next_write_insn (self)) == NULL)
return FALSE;
gum_arm64_relocator_increment_outpos (self);

ctx.len = ctx.insn->length;
ctx.raw_insn = ctx.insn->address;
ctx.raw_insn = GUINT32_FROM_LE (*((guint32 *) ctx.insn->address));
ctx.start = ctx.insn->address;
ctx.end = ctx.start + ctx.len;

ctx.output = self->output;

switch (ctx.insn->mnemonic)
{
case GUM_ARM64_ADR:
case GUM_ARM64_ADRP:
rewritten = gum_arm64_relocator_rewrite_adr (self, &ctx);
break;
default:
break;
}

if (!rewritten)
gum_arm64_writer_put_bytes (ctx.output, ctx.start, ctx.len);

Expand Down Expand Up @@ -216,7 +231,7 @@ gum_arm64_relocator_eoi (GumArm64Relocator * self)

gboolean
gum_arm64_relocator_can_relocate (gpointer address,
guint min_bytes)
guint min_bytes)
{
guint8 * buf;
GumArm64Writer cw;
Expand Down Expand Up @@ -245,8 +260,8 @@ gum_arm64_relocator_can_relocate (gpointer address,

guint
gum_arm64_relocator_relocate (gpointer from,
guint min_bytes,
gpointer to)
guint min_bytes,
gpointer to)
{
GumArm64Writer cw;
GumArm64Relocator rl;
Expand All @@ -270,3 +285,52 @@ gum_arm64_relocator_relocate (gpointer from,

return reloc_bytes;
}

static gboolean
gum_arm64_relocator_rewrite_adr (GumArm64Relocator * self,
GumCodeGenCtx * ctx)
{
GumArm64Reg reg;
guint64 imm_hi, imm_lo;
guint64 negative_mask;
union
{
gint64 i;
guint64 u;
} distance;
GumAddress absolute_target;

reg = ctx->raw_insn & 0x1f;
imm_hi = (ctx->raw_insn >> 5) & 0x7ffff;
imm_lo = (ctx->raw_insn >> 29) & 3;

if (ctx->insn->mnemonic == GUM_ARM64_ADR)
{
negative_mask = G_GUINT64_CONSTANT (0xffffffffffe00000);

if ((imm_hi & 0x40000) != 0)
distance.u = negative_mask | (imm_hi << 2) | imm_lo;
else
distance.u = (imm_hi << 2) | imm_lo;
}
else if (ctx->insn->mnemonic == GUM_ARM64_ADRP)
{
negative_mask = G_GUINT64_CONSTANT (0xfffffffe00000000);

if ((imm_hi & 0x40000) != 0)
distance.u = negative_mask | (imm_hi << 14) | (imm_lo << 12);
else
distance.u = (imm_hi << 14) | (imm_lo << 12);
}
else
{
g_assert_not_reached ();
}

absolute_target = ctx->insn->pc + distance.i;

gum_arm64_writer_put_ldr_reg_address (ctx->output, reg,
absolute_target);

return TRUE;
}
1 change: 1 addition & 0 deletions gum/arch-arm64/gumarm64relocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct _GumArm64Relocator
{
const guint8 * input_start;
const guint8 * input_cur;
GumAddress input_pc;
GumArm64Instruction * input_insns;
GumArm64Writer * output;

Expand Down
4 changes: 3 additions & 1 deletion tests/core/arch-arm64/arm64relocator-fixture.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test_arm64_relocator_fixture_setup (TestArm64RelocatorFixture * fixture,
fixture->output = (guint8 *) gum_alloc_n_pages (1, GUM_PAGE_RW);

gum_arm64_writer_init (&fixture->aw, fixture->output);
fixture->aw.pc = 1024;
}

static void
Expand All @@ -61,7 +62,8 @@ test_arm64_relocator_fixture_teardown (TestArm64RelocatorFixture * fixture,
static const guint8 cleared_outbuf[TEST_OUTBUF_SIZE] = { 0, };

#define SETUP_RELOCATOR_WITH(CODE) \
gum_arm64_relocator_init (&fixture->rl, CODE, &fixture->aw)
gum_arm64_relocator_init (&fixture->rl, CODE, &fixture->aw); \
fixture->rl.input_pc = 2048

#define assert_outbuf_still_zeroed_from_offset(OFF) \
g_assert_cmpint (memcmp (fixture->output + OFF, cleared_outbuf + OFF, \
Expand Down
60 changes: 60 additions & 0 deletions tests/core/arch-arm64/arm64relocator.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

TEST_LIST_BEGIN (arm64relocator)
TESTENTRY (one_to_one)
TESTENTRY (adr_should_be_rewritten)
TESTENTRY (adrp_should_be_rewritten)
TEST_LIST_END ()

TESTCASE (one_to_one)
Expand Down Expand Up @@ -52,3 +54,61 @@ TESTCASE (one_to_one)
g_assert (!gum_arm64_relocator_write_one (&fixture->rl));
}

TESTCASE (adr_should_be_rewritten)
{
const guint32 input[] = {
GUINT32_TO_LE (0x5000a721) /* adr x1, 0x14e6 */
};
const guint32 expected_output_instructions[] = {
GUINT32_TO_LE (0x58000021), /* ldr x1, [pc, #4] */
0xffffffff, /* <calculated PC */
0xffffffff /* goes here> */
};
gchar expected_output[3 * sizeof (guint32)];
guint64 calculated_pc;
const GumArm64Instruction * insn;

SETUP_RELOCATOR_WITH (input);

memcpy (expected_output, expected_output_instructions,
sizeof (expected_output_instructions));
calculated_pc = fixture->rl.input_pc + 0x14e6;
*((guint64 *) (expected_output + 4)) = GUINT64_TO_LE (calculated_pc);

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

TESTCASE (adrp_should_be_rewritten)
{
const guint32 input[] = {
GUINT32_TO_LE (0xd000a723) /* adrp x3, 0x14e6000 */
};
const guint32 expected_output_instructions[] = {
GUINT32_TO_LE (0x58000023), /* ldr x3, [pc, #4] */
0xffffffff, /* <calculated PC */
0xffffffff /* goes here> */
};
gchar expected_output[3 * sizeof (guint32)];
guint64 calculated_pc;
const GumArm64Instruction * insn;

SETUP_RELOCATOR_WITH (input);

memcpy (expected_output, expected_output_instructions,
sizeof (expected_output_instructions));
calculated_pc = fixture->rl.input_pc + 0x14e6000;
*((guint64 *) (expected_output + 4)) = GUINT64_TO_LE (calculated_pc);

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

0 comments on commit 3635815

Please sign in to comment.