From 6e93dc0be825e88bd4f724c1ef67e522c97b4bdf Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 8 Jul 2016 10:47:21 +0300 Subject: [PATCH] ARM: separate instructions decoding move ARM instruction decoding (kprobe -> probes) move THUMB instruction deconding (uprobe -> probes) Change-Id: Ief70952068b9a607d675ea797b186605e5b8950b Signed-off-by: Vyacheslav Cherkashin --- arch/arm/probes/decode_arm_old.h | 128 +++++ .../swap-asm => arch/arm/probes}/decode_thumb.c | 45 +- .../swap-asm => arch/arm/probes}/decode_thumb.h | 2 +- arch/arm/probes/decode_thumb_old.h | 282 ++++++++++ arch/arm/probes/probes_arm.c | 283 +++++++++++ .../thumb_tramps.h => arch/arm/probes/probes_arm.h | 19 +- arch/arm/probes/probes_thumb.c | 565 +++++++++++++++++++++ arch/arm/probes/probes_thumb.h | 34 ++ arch/arm/probes/tramps_arm.c | 86 ++++ .../arm/probes/tramps_arm.h | 12 +- arch/arm/probes/tramps_thumb.c | 169 ++++++ .../arm/probes/tramps_thumb.h | 12 +- kprobe/Kbuild | 7 +- kprobe/arch/arm/swap-asm/swap_kprobes.c | 275 +--------- kprobe/arch/arm/swap-asm/swap_kprobes.h | 375 -------------- kprobe/arch/arm/swap-asm/trampoline_arm.S | 63 --- uprobe/Kbuild | 6 +- uprobe/arch/arm/swap-asm/swap_uprobes.c | 550 +------------------- uprobe/arch/arm/swap-asm/thumb_tramps.c | 69 --- uprobe/arch/arm/swap-asm/trampoline_thumb.S | 134 ----- 20 files changed, 1641 insertions(+), 1475 deletions(-) create mode 100644 arch/arm/probes/decode_arm_old.h rename {uprobe/arch/arm/swap-asm => arch/arm/probes}/decode_thumb.c (83%) rename {uprobe/arch/arm/swap-asm => arch/arm/probes}/decode_thumb.h (97%) create mode 100644 arch/arm/probes/decode_thumb_old.h create mode 100644 arch/arm/probes/probes_arm.c rename uprobe/arch/arm/swap-asm/thumb_tramps.h => arch/arm/probes/probes_arm.h (64%) create mode 100644 arch/arm/probes/probes_thumb.c create mode 100644 arch/arm/probes/probes_thumb.h create mode 100644 arch/arm/probes/tramps_arm.c rename kprobe/arch/arm/swap-asm/trampoline_arm.h => arch/arm/probes/tramps_arm.h (86%) create mode 100644 arch/arm/probes/tramps_thumb.c rename uprobe/arch/arm/swap-asm/trampoline_thumb.h => arch/arm/probes/tramps_thumb.h (86%) delete mode 100644 kprobe/arch/arm/swap-asm/trampoline_arm.S delete mode 100644 uprobe/arch/arm/swap-asm/thumb_tramps.c delete mode 100644 uprobe/arch/arm/swap-asm/trampoline_thumb.S diff --git a/arch/arm/probes/decode_arm_old.h b/arch/arm/probes/decode_arm_old.h new file mode 100644 index 0000000..ae6b6c8 --- /dev/null +++ b/arch/arm/probes/decode_arm_old.h @@ -0,0 +1,128 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +#ifndef _SWAP_ASM_DECODE_ARM_OLD_H +#define _SWAP_ASM_DECODE_ARM_OLD_H + + +#define ARM_INSN_MATCH(name, insn) \ + ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name) +#define ARM_INSN_REG_RN(insn) ((insn & 0x000F0000) >> 16) +#define ARM_INSN_REG_SET_RN(insn, nreg) { insn &= ~0x000F0000; insn |= nreg << 16; } +#define ARM_INSN_REG_RD(insn) ((insn & 0x0000F000) >> 12) +#define ARM_INSN_REG_SET_RD(insn, nreg) { insn &= ~0x0000F000; insn |= nreg << 12; } +#define ARM_INSN_REG_RS(insn) ((insn & 0x00000F00) >> 8) +#define ARM_INSN_REG_SET_RS(insn, nreg) { insn &= ~0x00000F00; insn |= nreg << 8; } +#define ARM_INSN_REG_RM(insn) (insn & 0x0000000F) +#define ARM_INSN_REG_SET_RM(insn, nreg) { insn &= ~0x0000000F; insn |= nreg; } +#define ARM_INSN_REG_MR(insn, nreg) (insn & (1 << nreg)) +#define ARM_INSN_REG_SET_MR(insn, nreg) { insn |= (1 << nreg); } +#define ARM_INSN_REG_CLEAR_MR(insn, nreg) { insn &= ~(1 << nreg); } + + +/* Undefined */ +#define MASK_ARM_INSN_UNDEF 0x0FF00000 +#define PTRN_ARM_INSN_UNDEF 0x03000000 + +/* Architecturally undefined */ +#define MASK_ARM_INSN_AUNDEF 0x0FF000F0 +#define PTRN_ARM_INSN_AUNDEF 0x07F000F0 + +/* Branches */ +#define MASK_ARM_INSN_B 0x0F000000 +#define PTRN_ARM_INSN_B 0x0A000000 + +#define MASK_ARM_INSN_BL 0x0F000000 +#define PTRN_ARM_INSN_BL 0x0B000000 + +#define MASK_ARM_INSN_BLX1 0xFE000000 +#define PTRN_ARM_INSN_BLX1 0xFA000000 + +#define MASK_ARM_INSN_BLX2 0x0FF000F0 +#define PTRN_ARM_INSN_BLX2 0x01200030 + +#define MASK_ARM_INSN_BX 0x0FF000F0 +#define PTRN_ARM_INSN_BX 0x01200010 + +#define MASK_ARM_INSN_BXJ 0x0FF000F0 +#define PTRN_ARM_INSN_BXJ 0x01200020 + +/* Software interrupts */ +#define MASK_ARM_INSN_SWI 0x0F000000 +#define PTRN_ARM_INSN_SWI 0x0F000000 + +/* Break */ +#define MASK_ARM_INSN_BREAK 0xFFF000F0 +#define PTRN_ARM_INSN_BREAK 0xE1200070 +/* A8-56 ARM DDI 046B if cond != ‘1110’ then UNPREDICTABLE; */ + +/* CLZ */ +#define MASK_ARM_INSN_CLZ 0x0FFF0FF0 +#define PTRN_ARM_INSN_CLZ 0x016F0F10 + +/* Data processing immediate shift */ +#define MASK_ARM_INSN_DPIS 0x0E000010 +#define PTRN_ARM_INSN_DPIS 0x00000000 + +/* Data processing register shift */ +#define MASK_ARM_INSN_DPRS 0x0E000090 +#define PTRN_ARM_INSN_DPRS 0x00000010 + +/* Data processing immediate */ +#define MASK_ARM_INSN_DPI 0x0E000000 +#define PTRN_ARM_INSN_DPI 0x02000000 + +/* Load immediate offset */ +#define MASK_ARM_INSN_LIO 0x0E100000 +#define PTRN_ARM_INSN_LIO 0x04100000 + +/* Store immediate offset */ +#define MASK_ARM_INSN_SIO MASK_ARM_INSN_LIO +#define PTRN_ARM_INSN_SIO 0x04000000 + +/* Load register offset */ +#define MASK_ARM_INSN_LRO 0x0E100010 +#define PTRN_ARM_INSN_LRO 0x06100000 + +/* Store register offset */ +#define MASK_ARM_INSN_SRO MASK_ARM_INSN_LRO +#define PTRN_ARM_INSN_SRO 0x06000000 + +/* Load multiple */ +#define MASK_ARM_INSN_LM 0x0E100000 +#define PTRN_ARM_INSN_LM 0x08100000 + +/* Store multiple */ +#define MASK_ARM_INSN_SM MASK_ARM_INSN_LM +#define PTRN_ARM_INSN_SM 0x08000000 + + +/* Coprocessor load/store and double register transfers */ +#define MASK_ARM_INSN_CLS 0x0E000000 +#define PTRN_ARM_INSN_CLS 0x0C000000 + +/* Coprocessor register transfers */ +#define MASK_ARM_INSN_CRT 0x0F000010 +#define PTRN_ARM_INSN_CRT 0x0E000010 + + +#endif /* _SWAP_ASM_DECODE_ARM_OLD_H */ diff --git a/uprobe/arch/arm/swap-asm/decode_thumb.c b/arch/arm/probes/decode_thumb.c similarity index 83% rename from uprobe/arch/arm/swap-asm/decode_thumb.c rename to arch/arm/probes/decode_thumb.c index 9e35adb..3fe96c7 100644 --- a/uprobe/arch/arm/swap-asm/decode_thumb.c +++ b/arch/arm/probes/decode_thumb.c @@ -24,7 +24,7 @@ #include #include #include "decode_thumb.h" -#include "thumb_tramps.h" +#include "tramps_thumb.h" #define GET_BIT(x, n) ((x >> n) & 0x1) @@ -46,6 +46,49 @@ typedef union thumb_insn { typedef int (*decode_handler_t)(thumb_insn_t insn, struct decode_info *info); +static void make_def(void *tramp, unsigned long insn, + unsigned long vaddr, bool t2) +{ + const unsigned long URET_BP = 0xdeff; /* breakpoint for uretprobe */ + unsigned long ret_addr; + unsigned short *tr = tramp; + + /* + * thumb - +2 + * thumb2 - +4 + */ + ret_addr = vaddr + (2 << t2); + tr[4] = insn & 0x0000ffff; + if (t2) + tr[5] = insn >> 16; + + tr[13] = URET_BP; + tr[16] = (ret_addr & 0x0000ffff) | 0x1; + tr[17] = ret_addr >> 16; +} + +static void tt_make_common(void *tramp, unsigned long insn, + unsigned long vaddr, bool t2) +{ + memcpy(tramp, gen_insn_execbuf_thumb, 4 * UPROBES_TRAMP_LEN); + make_def(tramp, insn, vaddr, t2); +} + +static void tt_make_pc_deps(void *tramp, unsigned long mod_insn, + unsigned long vaddr, bool t2) +{ + unsigned long pc_val = vaddr + 4; + unsigned short *tr = tramp; + + memcpy(tramp, pc_dep_insn_execbuf_thumb, 4 * UPROBES_TRAMP_LEN); + make_def(tramp, mod_insn, vaddr, t2); + + /* save PC value */ + tr[14] = pc_val & 0x0000ffff; + tr[15] = pc_val >> 16; +} + + static bool bad_reg(int n) { return n == 13 || n == 15; diff --git a/uprobe/arch/arm/swap-asm/decode_thumb.h b/arch/arm/probes/decode_thumb.h similarity index 97% rename from uprobe/arch/arm/swap-asm/decode_thumb.h rename to arch/arm/probes/decode_thumb.h index 8c1142a..b33c305 100644 --- a/uprobe/arch/arm/swap-asm/decode_thumb.h +++ b/arch/arm/probes/decode_thumb.h @@ -24,7 +24,7 @@ #define _ARM_DECODE_THUMB_H -#include "swap_uprobes.h" +#include struct decode_info { diff --git a/arch/arm/probes/decode_thumb_old.h b/arch/arm/probes/decode_thumb_old.h new file mode 100644 index 0000000..df1faba --- /dev/null +++ b/arch/arm/probes/decode_thumb_old.h @@ -0,0 +1,282 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +#ifndef _SWAP_ASM_DECODE_THUMB_OLD_H +#define _SWAP_ASM_DECODE_THUMB_OLD_H + + +/* == THUMB == */ +#define THUMB_INSN_MATCH(name, insn) \ + (((insn & 0x0000FFFF) & MASK_THUMB_INSN_##name) == \ + PTRN_THUMB_INSN_##name) + + +/* Undefined */ +#define MASK_THUMB_INSN_UNDEF 0xFE00 +#define PTRN_THUMB_INSN_UNDEF 0xDE00 + +/* Branches */ +#define MASK_THUMB_INSN_B1 0xF000 +#define PTRN_THUMB_INSN_B1 0xD000 /* b label */ + +#define MASK_THUMB_INSN_B2 0xF800 +#define PTRN_THUMB_INSN_B2 0xE000 /* b label */ + +#define MASK_THUMB_INSN_CBZ 0xF500 +#define PTRN_THUMB_INSN_CBZ 0xB100 /* CBZ/CBNZ */ + +#define MASK_THUMB_INSN_BLX2 0xFF80 /* blx reg */ +#define PTRN_THUMB_INSN_BLX2 0x4780 + +#define MASK_THUMB_INSN_BX 0xFF80 +#define PTRN_THUMB_INSN_BX 0x4700 + +/* Software interrupts */ +#define MASK_THUMB_INSN_SWI 0xFF00 +#define PTRN_THUMB_INSN_SWI 0xDF00 + +/* Break */ +#define MASK_THUMB_INSN_BREAK 0xFF00 +#define PTRN_THUMB_INSN_BREAK 0xBE00 + +/* Data processing immediate */ +#define MASK_THUMB_INSN_DP 0xFC00 +#define PTRN_THUMB_INSN_DP 0x4000 + +#define MASK_THUMB_INSN_APC 0xF800 +#define PTRN_THUMB_INSN_APC 0xA000 /* ADD Rd, [PC, # * 4] */ + +#define MASK_THUMB_INSN_MOV3 0xFF00 +#define PTRN_THUMB_INSN_MOV3 0x4600 /* MOV Rd, PC */ + +/* Load immediate offset */ +#define MASK_THUMB_INSN_LIO1 0xF800 +#define PTRN_THUMB_INSN_LIO1 0x6800 /* LDR */ + +#define MASK_THUMB_INSN_LIO2 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_LIO2 0x7800 /* LDRB */ + +#define MASK_THUMB_INSN_LIO3 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_LIO3 0x8800 /* LDRH */ + +#define MASK_THUMB_INSN_LIO4 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_LIO4 0x9800 /* LDR SP relative */ + +/* Store immediate offset */ +#define MASK_THUMB_INSN_SIO1 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO1 0x6000 /* STR */ + +#define MASK_THUMB_INSN_SIO2 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO2 0x7000 /* STRB */ + +#define MASK_THUMB_INSN_SIO3 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO3 0x8000 /* STRH */ + +#define MASK_THUMB_INSN_SIO4 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO4 0x9000 /* STR SP relative */ + +/* Load register offset */ +#define MASK_THUMB_INSN_LRO1 0xFE00 +#define PTRN_THUMB_INSN_LRO1 0x5600 /* LDRSB */ + +#define MASK_THUMB_INSN_LRO2 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO2 0x5800 /* LDR */ + +#define MASK_THUMB_INSN_LRO3 0xf800 +#define PTRN_THUMB_INSN_LRO3 0x4800 /* LDR Rd, [PC, # * 4] */ + +#define MASK_THUMB_INSN_LRO4 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO4 0x5A00 /* LDRH */ + +#define MASK_THUMB_INSN_LRO5 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO5 0x5C00 /* LDRB */ + +#define MASK_THUMB_INSN_LRO6 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO6 0x5E00 /* LDRSH */ + +/* Store register offset */ +#define MASK_THUMB_INSN_SRO1 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_SRO1 0x5000 /* STR */ + +#define MASK_THUMB_INSN_SRO2 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_SRO2 0x5200 /* STRH */ + +#define MASK_THUMB_INSN_SRO3 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_SRO3 0x5400 /* STRB */ + + +/* == THUMB2 == */ +#define THUMB2_INSN_MATCH(name, insn) \ + ((insn & MASK_THUMB2_INSN_##name) == PTRN_THUMB2_INSN_##name) + +#define THUMB2_INSN_REG_RT(insn) ((insn & 0xf0000000) >> 28) +#define THUMB2_INSN_REG_RT2(insn) ((insn & 0x0f000000) >> 24) +#define THUMB2_INSN_REG_RN(insn) (insn & 0x0000000f) +#define THUMB2_INSN_REG_RD(insn) ((insn & 0x0f000000) >> 24) +#define THUMB2_INSN_REG_RM(insn) ((insn & 0x000f0000) >> 16) + + +/* Branches */ +#define MASK_THUMB2_INSN_B1 0xD000F800 +#define PTRN_THUMB2_INSN_B1 0x8000F000 + +#define MASK_THUMB2_INSN_B2 0xD000F800 +#define PTRN_THUMB2_INSN_B2 0x9000F000 + +#define MASK_THUMB2_INSN_BL 0xD000F800 +#define PTRN_THUMB2_INSN_BL 0xD000F000 /* bl imm swapped */ + +#define MASK_THUMB2_INSN_BLX1 0xD001F800 +#define PTRN_THUMB2_INSN_BLX1 0xC000F000 + +#define MASK_THUMB2_INSN_BXJ 0xD000FFF0 +#define PTRN_THUMB2_INSN_BXJ 0x8000F3C0 + +/* Data processing register shift */ +#define MASK_THUMB2_INSN_DPRS 0xFFE00000 +#define PTRN_THUMB2_INSN_DPRS 0xEA000000 + +/* Data processing immediate */ +#define MASK_THUMB2_INSN_DPI 0xFBE08000 +#define PTRN_THUMB2_INSN_DPI 0xF2000000 + +#define MASK_THUMB2_INSN_RSBW 0x8000fbe0 +#define PTRN_THUMB2_INSN_RSBW 0x0000f1c0 /* RSB{S}.W Rd,Rn,# */ + +#define MASK_THUMB2_INSN_RORW 0xf0f0ffe0 +#define PTRN_THUMB2_INSN_RORW 0xf000fa60 /* ROR{S}.W Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_ROR 0x0030ffef +#define PTRN_THUMB2_INSN_ROR 0x0030ea4f /* ROR{S} Rd, Rm, # */ + +#define MASK_THUMB2_INSN_LSLW1 0xf0f0ffe0 +#define PTRN_THUMB2_INSN_LSLW1 0xf000fa00 /* LSL{S}.W Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_LSLW2 0x0030ffef +#define PTRN_THUMB2_INSN_LSLW2 0x0000ea4f /* LSL{S}.W Rd, Rm, #*/ + +#define MASK_THUMB2_INSN_LSRW1 0xf0f0ffe0 +#define PTRN_THUMB2_INSN_LSRW1 0xf000fa20 /* LSR{S}.W Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_LSRW2 0x0030ffef +#define PTRN_THUMB2_INSN_LSRW2 0x0010ea4f /* LSR{S}.W Rd, Rm, # */ + +#define MASK_THUMB2_INSN_TEQ1 0x8f00fbf0 +#define PTRN_THUMB2_INSN_TEQ1 0x0f00f090 /* TEQ Rn, # */ + +#define MASK_THUMB2_INSN_TEQ2 0x0f00fff0 +#define PTRN_THUMB2_INSN_TEQ2 0x0f00ea90 /* TEQ Rn, Rm{,} */ + +#define MASK_THUMB2_INSN_TST1 0x8f00fbf0 +#define PTRN_THUMB2_INSN_TST1 0x0f00f010 /* TST Rn, # */ + +#define MASK_THUMB2_INSN_TST2 0x0f00fff0 +#define PTRN_THUMB2_INSN_TST2 0x0f00ea10 /* TST Rn, Rm{,} */ + +/* Load immediate offset */ +#define MASK_THUMB2_INSN_LDRW 0x0000fff0 +#define PTRN_THUMB2_INSN_LDRW 0x0000f850 /* LDR.W Rt, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRW1 MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRW1 0x0000f8d0 /* LDR.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRBW MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRBW 0x0000f810 /* LDRB.W Rt, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRBW1 MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRBW1 0x0000f890 /* LDRB.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRHW MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRHW 0x0000f830 /* LDRH.W Rt, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRHW1 MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRHW1 0x0000f8b0 /* LDRH.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRD 0x0000fed0 +#define PTRN_THUMB2_INSN_LDRD 0x0000e850 /* LDRD Rt, Rt2, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRD1 MASK_THUMB2_INSN_LDRD +#define PTRN_THUMB2_INSN_LDRD1 0x0000e8d0 /* LDRD Rt, Rt2, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRWL 0x0fc0fff0 +#define PTRN_THUMB2_INSN_LDRWL 0x0000f850 /* LDR.W Rt, [Rn,Rm,LSL #] */ + +#define MASK_THUMB2_INSN_LDREX 0x0f00ffff +#define PTRN_THUMB2_INSN_LDREX 0x0f00e85f /* LDREX Rt, [PC, #] */ + +#define MASK_THUMB2_INSN_MUL 0xf0f0fff0 +#define PTRN_THUMB2_INSN_MUL 0xf000fb00 /* MUL Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_DP 0x0000ff00 +#define PTRN_THUMB2_INSN_DP 0x0000eb00 /* ADD/SUB/SBC/...Rd,Rn,Rm{,} */ + +/* Store immediate offset */ +#define MASK_THUMB2_INSN_STRW 0x0fc0fff0 +#define PTRN_THUMB2_INSN_STRW 0x0000f840 /* STR.W Rt,[Rn,Rm,{LSL #}] */ + +#define MASK_THUMB2_INSN_STRW1 0x0000fff0 +#define PTRN_THUMB2_INSN_STRW1 0x0000f8c0 /* STR.W Rt, [Rn, #imm12] + * STR.W Rt, [PC, #imm12] shall be + * skipped, because it hangs + * on Tegra. WTF */ + +#define MASK_THUMB2_INSN_STRHW MASK_THUMB2_INSN_STRW +#define PTRN_THUMB2_INSN_STRHW 0x0000f820 /* STRH.W Rt,[Rn,Rm,{LSL #}] */ + +#define MASK_THUMB2_INSN_STRHW1 0x0000fff0 +#define PTRN_THUMB2_INSN_STRHW1 0x0000f8a0 /* STRH.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_STRHT 0x0f00fff0 /* strht r1, [pc, #imm] illegal + * instruction on Tegra. WTF */ +#define PTRN_THUMB2_INSN_STRHT 0x0e00f820 /* STRHT Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_STRT 0x0f00fff0 +#define PTRN_THUMB2_INSN_STRT 0x0e00f840 /* STRT Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_STRBW MASK_THUMB2_INSN_STRW +#define PTRN_THUMB2_INSN_STRBW 0x0000f800 /* STRB.W Rt,[Rn,Rm,{LSL #}] */ + +#define MASK_THUMB2_INSN_STRBW1 0x0000fff0 +#define PTRN_THUMB2_INSN_STRBW1 0x0000f880 /* STRB.W Rt, [Rn, #] + * STRB.W Rt, [PC, #imm12] shall be + * skipped, because it hangs + * on Tegra. WTF */ + +#define MASK_THUMB2_INSN_STRBT 0x0f00fff0 +#define PTRN_THUMB2_INSN_STRBT 0x0e00f800 /* STRBT Rt, [Rn, #}] */ + +#define MASK_THUMB2_INSN_STRD 0x0000fe50 +#define PTRN_THUMB2_INSN_STRD 0x0000e840 /* STR{D,EX,EXB,EXH,EXD} Rt, Rt2, [Rn, #] */ + +/* Load register offset */ +#define MASK_THUMB2_INSN_ADR 0x8000fa1f +#define PTRN_THUMB2_INSN_ADR 0x0000f20f + +/* Load multiple */ +#define MASK_THUMB2_INSN_LDMIA 0x8000ffd0 +#define PTRN_THUMB2_INSN_LDMIA 0x8000e890 /* LDMIA(.W) Rn(!),{Rx-PC} */ + +#define MASK_THUMB2_INSN_LDMDB 0x8000ffd0 +#define PTRN_THUMB2_INSN_LDMDB 0x8000e910 /* LDMDB(.W) Rn(!), {Rx-PC} */ + + +#endif /* _SWAP_ASM_DECODE_THUMB_OLD_H */ diff --git a/arch/arm/probes/probes_arm.c b/arch/arm/probes/probes_arm.c new file mode 100644 index 0000000..d310ddb --- /dev/null +++ b/arch/arm/probes/probes_arm.c @@ -0,0 +1,283 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +#include +#include +#include /* nedded for printk.h */ +#include /* needed for printk.h */ +#include +#include +#include "tramps_arm.h" +#include "decode_arm_old.h" + + +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) + + +static unsigned long get_addr_b(unsigned long insn, unsigned long addr) +{ + /* real position less then PC by 8 */ + return ((long)addr + 8 + branch_displacement(insn)); +} + +static int prep_pc_dep_insn_execbuf(unsigned long *insns, + unsigned long insn, int uregs) +{ + int i; + + if (uregs & 0x10) { + int reg_mask = 0x1; + /* search in reg list */ + for (i = 0; i < 13; i++, reg_mask <<= 1) { + if (!(insn & reg_mask)) + break; + } + } else { + for (i = 0; i < 13; i++) { + if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i)) + continue; + if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i)) + continue; + if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i)) + continue; + if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i)) + continue; + break; + } + } + + if (i == 13) { + pr_err("there are no free register %x in insn %lx!", + uregs, insn); + return -EINVAL; + } + + /* set register to save */ + ARM_INSN_REG_SET_RD(insns[0], i); + /* set register to load address to */ + ARM_INSN_REG_SET_RD(insns[1], i); + /* set instruction to execute and patch it */ + if (uregs & 0x10) { + ARM_INSN_REG_CLEAR_MR(insn, 15); + ARM_INSN_REG_SET_MR(insn, i); + } else { + if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15)) + ARM_INSN_REG_SET_RN(insn, i); + if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15)) + ARM_INSN_REG_SET_RD(insn, i); + if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15)) + ARM_INSN_REG_SET_RS(insn, i); + if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15)) + ARM_INSN_REG_SET_RM(insn, i); + } + + insns[UPROBES_TRAMP_INSN_IDX] = insn; + /* set register to restore */ + ARM_INSN_REG_SET_RD(insns[3], i); + + return 0; +} + +static int arch_check_insn_arm(unsigned long insn) +{ + /* check instructions that can change PC by nature */ + if ( + /* ARM_INSN_MATCH(UNDEF, insn) || */ + ARM_INSN_MATCH(AUNDEF, insn) || + ARM_INSN_MATCH(SWI, insn) || + ARM_INSN_MATCH(BREAK, insn) || + ARM_INSN_MATCH(BXJ, insn)) { + goto bad_insn; +#ifndef CONFIG_CPU_V7 + /* check instructions that can write result to PC */ + } else if ((ARM_INSN_MATCH(DPIS, insn) || + ARM_INSN_MATCH(DPRS, insn) || + ARM_INSN_MATCH(DPI, insn) || + ARM_INSN_MATCH(LIO, insn) || + ARM_INSN_MATCH(LRO, insn)) && + (ARM_INSN_REG_RD(insn) == 15)) { + goto bad_insn; +#endif /* CONFIG_CPU_V7 */ + /* check special instruction loads store multiple registers */ + } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) && + /* store PC or load to PC */ + (ARM_INSN_REG_MR(insn, 15) || + /* store/load with PC update */ + ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) { + goto bad_insn; + } + + return 0; + +bad_insn: + return -EFAULT; +} + +static int make_branch_tarmpoline(unsigned long addr, unsigned long insn, + unsigned long *tramp) +{ + int ok = 0; + + /* B */ + if (ARM_INSN_MATCH(B, insn) && + !ARM_INSN_MATCH(BLX1, insn)) { + /* B check can be false positive on BLX1 instruction */ + memcpy(tramp, b_cond_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[0] |= insn & 0xf0000000; + tramp[6] = get_addr_b(insn, addr); + tramp[7] = addr + 4; + ok = 1; + /* BX, BLX (Rm) */ + } else if (ARM_INSN_MATCH(BX, insn) || + ARM_INSN_MATCH(BLX2, insn)) { + memcpy(tramp, b_r_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[0] = insn; + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[7] = addr + 4; + ok = 1; + /* BL, BLX (Off) */ + } else if (ARM_INSN_MATCH(BLX1, insn)) { + memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[0] |= 0xe0000000; + tramp[1] |= 0xe0000000; + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[6] = get_addr_b(insn, addr) + + 2 * (insn & 01000000) + 1; /* jump to thumb */ + tramp[7] = addr + 4; + ok = 1; + /* BL */ + } else if (ARM_INSN_MATCH(BL, insn)) { + memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[0] |= insn & 0xf0000000; + tramp[1] |= insn & 0xf0000000; + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[6] = get_addr_b(insn, addr); + tramp[7] = addr + 4; + ok = 1; + } + + return ok; +} + +/** + * @brief Creates ARM trampoline. + * + * @param addr Probe address. + * @param insn Instuction at this address. + * @param tramp Pointer to memory for trampoline. + * @return 0 on success, error code on error. + */ +int make_trampoline_arm(unsigned long addr, unsigned long insn, + unsigned long *tramp) +{ + int ret, uregs, pc_dep; + + if (addr & 0x03) { + pr_err("Error in %s at %d: attempt to register uprobe " + "at an unaligned address\n", __FILE__, __LINE__); + return -EINVAL; + } + + ret = arch_check_insn_arm(insn); + if (ret) + return ret; + + if (make_branch_tarmpoline(addr, insn, tramp)) + return 0; + + uregs = pc_dep = 0; + /* Rm */ + if (ARM_INSN_MATCH(CLZ, insn)) { + uregs = 0xa; + if (ARM_INSN_REG_RM(insn) == 15) + pc_dep = 1; + /* Rn, Rm ,Rd */ + } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) || + ARM_INSN_MATCH(SRO, insn)) { + uregs = 0xb; + if ((ARM_INSN_REG_RN(insn) == 15) || + (ARM_INSN_REG_RM(insn) == 15) || + (ARM_INSN_MATCH(SRO, insn) && + (ARM_INSN_REG_RD(insn) == 15))) { + pc_dep = 1; + } + /* Rn ,Rd */ + } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) || + ARM_INSN_MATCH(SIO, insn)) { + uregs = 0x3; + if ((ARM_INSN_REG_RN(insn) == 15) || + (ARM_INSN_MATCH(SIO, insn) && + (ARM_INSN_REG_RD(insn) == 15))) { + pc_dep = 1; + } + /* Rn, Rm, Rs */ + } else if (ARM_INSN_MATCH(DPRS, insn)) { + uregs = 0xd; + if ((ARM_INSN_REG_RN(insn) == 15) || + (ARM_INSN_REG_RM(insn) == 15) || + (ARM_INSN_REG_RS(insn) == 15)) { + pc_dep = 1; + } + /* register list */ + } else if (ARM_INSN_MATCH(SM, insn)) { + uregs = 0x10; + if (ARM_INSN_REG_MR(insn, 15)) + pc_dep = 1; + } + + /* check instructions that can write result to SP and uses PC */ + if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) { + pr_err("Error in %s at %d: instruction check failed (arm)\n", + __FILE__, __LINE__); + return -EFAULT; + } + + if (unlikely(uregs && pc_dep)) { + memcpy(tramp, pc_dep_insn_execbuf, KPROBES_TRAMP_LEN); + if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) { + pr_err("Error in %s at %d: failed " + "to prepare exec buffer for insn %lx!", + __FILE__, __LINE__, insn); + return -EINVAL; + } + + tramp[6] = addr + 8; + } else { + memcpy(tramp, gen_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[KPROBES_TRAMP_INSN_IDX] = insn; + } + + /* TODO: remove for kprobe */ + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[7] = addr + 4; + + return 0; +} + +int noret_arm(u32 opcode) +{ + return !!(ARM_INSN_MATCH(BL, opcode) || + ARM_INSN_MATCH(BLX1, opcode) || + ARM_INSN_MATCH(BLX2, opcode)); +} diff --git a/uprobe/arch/arm/swap-asm/thumb_tramps.h b/arch/arm/probes/probes_arm.h similarity index 64% rename from uprobe/arch/arm/swap-asm/thumb_tramps.h rename to arch/arm/probes/probes_arm.h index 0b45415..19f74df 100644 --- a/uprobe/arch/arm/swap-asm/thumb_tramps.h +++ b/arch/arm/probes/probes_arm.h @@ -13,24 +13,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) Samsung Electronics, 2015 + * Copyright (C) Samsung Electronics, 2016 * - * 2015 Vyacheslav Cherkashin + * 2016 Vyacheslav Cherkashin * */ -#ifndef _ARM_THUMB_TRAMPS_H -#define _ARM_THUMB_TRAMPS_H +#ifndef _SWAP_ASM_PROBES_ARM_H +#define _SWAP_ASM_PROBES_ARM_H -#include +int make_trampoline_arm(unsigned long addr, unsigned long insn, + unsigned long *tramp); +int noret_arm(u32 opcode); -void tt_make_common(void *tramp, unsigned long insn, - unsigned long vaddr, bool t2); -void tt_make_pc_deps(void *tramp, unsigned long mod_insn, - unsigned long vaddr, bool t2); - -#endif /* _ARM_THUMB_TRAMPS_H */ +#endif /* _SWAP_ASM_PROBES_ARM_H */ diff --git a/arch/arm/probes/probes_thumb.c b/arch/arm/probes/probes_thumb.c new file mode 100644 index 0000000..b6cd77c --- /dev/null +++ b/arch/arm/probes/probes_thumb.c @@ -0,0 +1,565 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +#include "tramps_thumb.h" +#include "decode_thumb.h" +#include "decode_thumb_old.h" + + +static inline long branch_t16_dest(unsigned long insn, unsigned int insn_addr) +{ + long offset = insn & 0x3ff; + offset -= insn & 0x400; + return insn_addr + 4 + offset * 2; +} + +static inline long branch_cond_t16_dest(unsigned long insn, + unsigned int insn_addr) +{ + long offset = insn & 0x7f; + offset -= insn & 0x80; + return insn_addr + 4 + offset * 2; +} + +static inline long branch_t32_dest(unsigned long insn, unsigned int insn_addr) +{ + unsigned int poff = insn & 0x3ff; + unsigned int offset = (insn & 0x07fe0000) >> 17; + + poff -= (insn & 0x400); + + if (insn & (1 << 12)) + return insn_addr + 4 + (poff << 12) + offset * 4; + + return (insn_addr + 4 + (poff << 12) + offset * 4) & ~3; +} + +static inline long cbz_t16_dest(unsigned long insn, unsigned int insn_addr) +{ + unsigned int i = (insn & 0x200) >> 3; + unsigned int offset = (insn & 0xf8) >> 2; + return insn_addr + 4 + i + offset; +} + +/* is instruction Thumb2 and NOT a branch, etc... */ +static int is_thumb2(unsigned long insn) +{ + return ((insn & 0xf800) == 0xe800 || + (insn & 0xf800) == 0xf000 || + (insn & 0xf800) == 0xf800); +} + +static int prep_pc_dep_insn_execbuf_thumb(unsigned long *insns, + unsigned long insn, int uregs) +{ + unsigned char mreg = 0; + unsigned char reg = 0; + + if (THUMB_INSN_MATCH(APC, insn) || + THUMB_INSN_MATCH(LRO3, insn)) { + reg = ((insn & 0xffff) & uregs) >> 8; + } else if (THUMB_INSN_MATCH(MOV3, insn)) { + if (((((unsigned char)insn) & 0xff) >> 3) == 15) + reg = (insn & 0xffff) & uregs; + else + return 0; + } else if (THUMB2_INSN_MATCH(ADR, insn)) { + reg = ((insn >> 16) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(LDRW, insn) || + THUMB2_INSN_MATCH(LDRW1, insn) || + THUMB2_INSN_MATCH(LDRHW, insn) || + THUMB2_INSN_MATCH(LDRHW1, insn) || + THUMB2_INSN_MATCH(LDRWL, insn)) { + reg = ((insn >> 16) & uregs) >> 12; + if (reg == 15) + return 0; + /* + * LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped + */ + } else if (THUMB2_INSN_MATCH(LDRBW, insn) || + THUMB2_INSN_MATCH(LDRBW1, insn) || + THUMB2_INSN_MATCH(LDREX, insn)) { + reg = ((insn >> 16) & uregs) >> 12; + } else if (THUMB2_INSN_MATCH(DP, insn)) { + reg = ((insn >> 16) & uregs) >> 12; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(RSBW, insn)) { + reg = ((insn >> 12) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(RORW, insn)) { + reg = ((insn >> 12) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(ROR, insn) || + THUMB2_INSN_MATCH(LSLW1, insn) || + THUMB2_INSN_MATCH(LSLW2, insn) || + THUMB2_INSN_MATCH(LSRW1, insn) || + THUMB2_INSN_MATCH(LSRW2, insn)) { + reg = ((insn >> 12) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(TEQ1, insn) || + THUMB2_INSN_MATCH(TST1, insn)) { + reg = 15; + } else if (THUMB2_INSN_MATCH(TEQ2, insn) || + THUMB2_INSN_MATCH(TST2, insn)) { + reg = THUMB2_INSN_REG_RM(insn); + } + + if ((THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn) || + THUMB2_INSN_MATCH(STRD, insn) || + THUMB2_INSN_MATCH(STRHT, insn) || + THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHW1, insn) || + THUMB2_INSN_MATCH(STRHW, insn)) && + THUMB2_INSN_REG_RT(insn) == 15) { + reg = THUMB2_INSN_REG_RT(insn); + } + + if (reg == 6 || reg == 7) { + *((unsigned short *)insns + 0) = + (*((unsigned short *)insns + 0) & 0x00ff) | + ((1 << mreg) | (1 << (mreg + 1))); + *((unsigned short *)insns + 1) = + (*((unsigned short *)insns + 1) & 0xf8ff) | (mreg << 8); + *((unsigned short *)insns + 2) = + (*((unsigned short *)insns + 2) & 0xfff8) | (mreg + 1); + *((unsigned short *)insns + 3) = + (*((unsigned short *)insns + 3) & 0xffc7) | (mreg << 3); + *((unsigned short *)insns + 7) = + (*((unsigned short *)insns + 7) & 0xf8ff) | (mreg << 8); + *((unsigned short *)insns + 8) = + (*((unsigned short *)insns + 8) & 0xffc7) | (mreg << 3); + *((unsigned short *)insns + 9) = + (*((unsigned short *)insns + 9) & 0xffc7) | + ((mreg + 1) << 3); + *((unsigned short *)insns + 10) = + (*((unsigned short *)insns + 10) & 0x00ff) | + ((1 << mreg) | (1 << (mreg + 1))); + } + + if (THUMB_INSN_MATCH(APC, insn)) { + /* ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4 */ + *((unsigned short *)insns + 4) = ((insn & 0xffff) | 0x800); + } else if (THUMB_INSN_MATCH(LRO3, insn)) { + /* LDR Rd, [PC, #immed_8*4] -> + * LDR Rd, [SP, #immed_8*4] */ + *((unsigned short *)insns + 4) = + ((insn & 0xffff) + 0x5000); + } else if (THUMB_INSN_MATCH(MOV3, insn)) { + /* MOV Rd, PC -> MOV Rd, SP */ + *((unsigned short *)insns + 4) = + ((insn & 0xffff) ^ 0x10); + } else if (THUMB2_INSN_MATCH(ADR, insn)) { + /* ADDW Rd,PC,#imm -> ADDW Rd,SP,#imm */ + insns[2] = (insn & 0xfffffff0) | 0x0d; + } else if (THUMB2_INSN_MATCH(LDRW, insn) || + THUMB2_INSN_MATCH(LDRBW, insn) || + THUMB2_INSN_MATCH(LDRHW, insn)) { + /* LDR.W Rt, [PC, #-] -> + * LDR.W Rt, [SP, #-] + * !!!!!!!!!!!!!!!!!!!!!!!! + * !!! imm_12 vs. imm_8 !!! + * !!!!!!!!!!!!!!!!!!!!!!!! */ + insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; + } else if (THUMB2_INSN_MATCH(LDRW1, insn) || + THUMB2_INSN_MATCH(LDRBW1, insn) || + THUMB2_INSN_MATCH(LDRHW1, insn) || + THUMB2_INSN_MATCH(LDRD, insn) || + THUMB2_INSN_MATCH(LDRD1, insn) || + THUMB2_INSN_MATCH(LDREX, insn)) { + /* LDRx.W Rt, [PC, #+] -> + * LDRx.W Rt, [SP, #+] + (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(MUL, insn)) { + /* MUL Rd, Rn, SP */ + insns[2] = (insn & 0xfff0ffff) | 0x000d0000; + } else if (THUMB2_INSN_MATCH(DP, insn)) { + if (THUMB2_INSN_REG_RM(insn) == 15) + /* DP Rd, Rn, PC */ + insns[2] = (insn & 0xfff0ffff) | 0x000d0000; + else if (THUMB2_INSN_REG_RN(insn) == 15) + /* DP Rd, PC, Rm */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(LDRWL, insn)) { + /* LDRx.W Rt, [PC, #] -> + * LDRx.W Rt, [SP, #+] + * (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(RSBW, insn)) { + /* RSB{S}.W Rd, PC, # -> RSB{S}.W Rd, SP, # */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(RORW, insn) || + THUMB2_INSN_MATCH(LSLW1, insn) || + THUMB2_INSN_MATCH(LSRW1, insn)) { + if ((THUMB2_INSN_REG_RM(insn) == 15) && + (THUMB2_INSN_REG_RN(insn) == 15)) + /* ROR.W Rd, PC, PC */ + insns[2] = (insn & 0xfffdfffd); + else if (THUMB2_INSN_REG_RM(insn) == 15) + /* ROR.W Rd, Rn, PC */ + insns[2] = (insn & 0xfff0ffff) | 0xd0000; + else if (THUMB2_INSN_REG_RN(insn) == 15) + /* ROR.W Rd, PC, Rm */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(ROR, insn) || + THUMB2_INSN_MATCH(LSLW2, insn) || + THUMB2_INSN_MATCH(LSRW2, insn)) { + /* ROR{S} Rd, PC, # -> ROR{S} Rd, SP, # */ + insns[2] = (insn & 0xfff0ffff) | 0xd0000; + } + + if (THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn)) { + /* STRx.W Rt, [Rn, SP] */ + insns[2] = (insn & 0xfff0ffff) | 0x000d0000; + } else if (THUMB2_INSN_MATCH(STRD, insn) || + THUMB2_INSN_MATCH(STRHT, insn) || + THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHW1, insn)) { + if (THUMB2_INSN_REG_RN(insn) == 15) + /* STRD/T/HT{.W} Rt, [SP, ...] */ + insns[2] = (insn & 0xfffffff0) | 0xd; + else + insns[2] = insn; + } else if (THUMB2_INSN_MATCH(STRHW, insn) && + (THUMB2_INSN_REG_RN(insn) == 15)) { + if (THUMB2_INSN_REG_RN(insn) == 15) + /* STRH.W Rt, [SP, #-] */ + insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; + else + insns[2] = insn; + } + + /* STRx PC, xxx */ + if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn) || + THUMB2_INSN_MATCH(STRD, insn) || + THUMB2_INSN_MATCH(STRHT, insn) || + THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHW1, insn) || + THUMB2_INSN_MATCH(STRHW, insn))) { + insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000; + } + + if (THUMB2_INSN_MATCH(TEQ1, insn) || + THUMB2_INSN_MATCH(TST1, insn)) { + /* TEQ SP, # */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(TEQ2, insn) || + THUMB2_INSN_MATCH(TST2, insn)) { + if ((THUMB2_INSN_REG_RN(insn) == 15) && + (THUMB2_INSN_REG_RM(insn) == 15)) + /* TEQ/TST PC, PC */ + insns[2] = (insn & 0xfffdfffd); + else if (THUMB2_INSN_REG_RM(insn) == 15) + /* TEQ/TST Rn, PC */ + insns[2] = (insn & 0xfff0ffff) | 0xd0000; + else if (THUMB2_INSN_REG_RN(insn) == 15) + /* TEQ/TST PC, Rm */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } + + return 0; +} + +static int arch_check_insn_thumb(unsigned long insn) +{ + int ret = 0; + + /* check instructions that can change PC */ + if (THUMB_INSN_MATCH(UNDEF, insn) || + THUMB2_INSN_MATCH(BLX1, insn) || + THUMB2_INSN_MATCH(BL, insn) || + THUMB_INSN_MATCH(SWI, insn) || + THUMB_INSN_MATCH(BREAK, insn) || + THUMB2_INSN_MATCH(B1, insn) || + THUMB2_INSN_MATCH(B2, insn) || + THUMB2_INSN_MATCH(BXJ, insn) || + (THUMB2_INSN_MATCH(ADR, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRW1, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRHW, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRHW1, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRWL, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + THUMB2_INSN_MATCH(LDMIA, insn) || + THUMB2_INSN_MATCH(LDMDB, insn) || + (THUMB2_INSN_MATCH(DP, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(RSBW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(RORW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(ROR, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSLW1, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSLW2, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSRW1, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSRW2, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */ + (THUMB2_INSN_MATCH(STRW1, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRBW1, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRHW1, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRHW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(LDRBW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(LDRHW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */ + (THUMB2_INSN_MATCH(LDRD, insn) || THUMB2_INSN_MATCH(LDRD1, insn) || + THUMB2_INSN_MATCH(STRD, insn))) { + ret = -EFAULT; + } + + return ret; +} + +static int do_make_trampoline_thumb(unsigned long vaddr, unsigned long insn, + unsigned long *tramp, size_t tramp_len) +{ + int ret; + int uregs = 0; + int pc_dep = 0; + unsigned int addr; + + ret = arch_check_insn_thumb(insn); + if (ret) + return ret; + + if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) { + uregs = 0x0700; /* 8-10 */ + pc_dep = 1; + } else if (THUMB_INSN_MATCH(MOV3, insn) && + (((((unsigned char)insn) & 0xff) >> 3) == 15)) { + /* MOV Rd, PC */ + uregs = 0x07; + pc_dep = 1; + } else if THUMB2_INSN_MATCH(ADR, insn) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if (((THUMB2_INSN_MATCH(LDRW, insn) || + THUMB2_INSN_MATCH(LDRW1, insn) || + THUMB2_INSN_MATCH(LDRBW, insn) || + THUMB2_INSN_MATCH(LDRBW1, insn) || + THUMB2_INSN_MATCH(LDRHW, insn) || + THUMB2_INSN_MATCH(LDRHW1, insn) || + THUMB2_INSN_MATCH(LDRWL, insn)) && + THUMB2_INSN_REG_RN(insn) == 15) || + THUMB2_INSN_MATCH(LDREX, insn) || + ((THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn) || + THUMB2_INSN_MATCH(STRHW, insn) || + THUMB2_INSN_MATCH(STRHW1, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RT(insn) == 15)) || + ((THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHT, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RT(insn) == 15))) { + uregs = 0xf000; /* Rt 12-15 */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(LDRD, insn) || + THUMB2_INSN_MATCH(LDRD1, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15)) { + uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(MUL, insn) && + THUMB2_INSN_REG_RM(insn) == 15) { + uregs = 0xf; + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(DP, insn) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0xf000; /* Rd 12-15 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(STRD, insn) && + ((THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_REG_RT(insn) == 15) || + THUMB2_INSN_REG_RT2(insn) == 15)) { + uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(RSBW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(RORW, insn) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0x0f00; + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(ROR, insn) || + THUMB2_INSN_MATCH(LSLW2, insn) || + THUMB2_INSN_MATCH(LSRW2, insn)) && + THUMB2_INSN_REG_RM(insn) == 15) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(LSLW1, insn) || + THUMB2_INSN_MATCH(LSRW1, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(TEQ1, insn) || + THUMB2_INSN_MATCH(TST1, insn)) && + THUMB2_INSN_REG_RN(insn) == 15) { + uregs = 0xf0000; /* Rn 0-3 (16-19) */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(TEQ2, insn) || + THUMB2_INSN_MATCH(TST2, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0xf0000; /* Rn 0-3 (16-19) */ + pc_dep = 1; + } + + if (unlikely(uregs && pc_dep)) { + memcpy(tramp, pc_dep_insn_execbuf_thumb, tramp_len); + prep_pc_dep_insn_execbuf_thumb(tramp, insn, uregs); + + addr = vaddr + 4; + *((unsigned short *)tramp + 13) = 0xdeff; + *((unsigned short *)tramp + 14) = addr & 0x0000ffff; + *((unsigned short *)tramp + 15) = addr >> 16; + if (!is_thumb2(insn)) { + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } else { + addr = vaddr + 4; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } + } else { + memcpy(tramp, gen_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + if (!is_thumb2(insn)) { + addr = vaddr + 2; + *((unsigned short *)tramp + 2) = insn; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } else { + addr = vaddr + 4; + tramp[1] = insn; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } + } + + if (THUMB_INSN_MATCH(B2, insn)) { + memcpy(tramp, b_off_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + addr = branch_t16_dest(insn, vaddr); + *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 15) = addr >> 16; + *((unsigned short *)tramp + 16) = 0; + *((unsigned short *)tramp + 17) = 0; + + } else if (THUMB_INSN_MATCH(B1, insn)) { + memcpy(tramp, b_cond_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + *((unsigned short *)tramp + 0) |= (insn & 0xf00); + addr = branch_cond_t16_dest(insn, vaddr); + *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 15) = addr >> 16; + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + + } else if (THUMB_INSN_MATCH(BLX2, insn) || + THUMB_INSN_MATCH(BX, insn)) { + memcpy(tramp, b_r_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + *((unsigned short *)tramp + 4) = insn; + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + + } else if (THUMB_INSN_MATCH(CBZ, insn)) { + memcpy(tramp, cbz_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + /* zero out original branch displacement (imm5 = 0; i = 0) */ + *((unsigned short *)tramp + 0) = insn & (~0x2f8); + /* replace it with 8 bytes offset in execbuf (imm5 = 0b00010) */ + *((unsigned short *)tramp + 0) |= 0x20; + addr = cbz_t16_dest(insn, vaddr); + *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 15) = addr >> 16; + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } + + return 0; +} + +int make_trampoline_thumb(struct uprobe *p, + unsigned long vaddr, unsigned long insn, + unsigned long *tramp, size_t tramp_len) +{ + int ret; + + ret = do_make_trampoline_thumb(vaddr, insn, tramp, tramp_len); + if (ret) { + struct decode_info info = { + .vaddr = vaddr, + .tramp = tramp, + .handeler = NULL, + }; + + ret = decode_thumb(insn, &info); + if (info.handeler) { + unsigned short *tr = (unsigned short *)tramp; + tr[13] = 0xdeff; /* breakpoint for uretprobe */ + p->ainsn.handler = info.handeler; + } + } + + return ret; +} + +int noret_thumb(u32 opcode) +{ + return !!(THUMB2_INSN_MATCH(BL, opcode) || + THUMB2_INSN_MATCH(BLX1, opcode) || + THUMB_INSN_MATCH(BLX2, opcode)); +} diff --git a/arch/arm/probes/probes_thumb.h b/arch/arm/probes/probes_thumb.h new file mode 100644 index 0000000..a8d2f97 --- /dev/null +++ b/arch/arm/probes/probes_thumb.h @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +#ifndef _SWAP_ASM_PROBES_THUMB_H +#define _SWAP_ASM_PROBES_THUMB_H + + +int make_trampoline_thumb(struct uprobe *p, + unsigned long vaddr, unsigned long insn, + unsigned long *tramp, size_t tramp_len); + +int noret_thumb(u32 opcode); + + +#endif /* _SWAP_ASM_PROBES_THUMB_H */ diff --git a/arch/arm/probes/tramps_arm.c b/arch/arm/probes/tramps_arm.c new file mode 100644 index 0000000..84d978d --- /dev/null +++ b/arch/arm/probes/tramps_arm.c @@ -0,0 +1,86 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +__asm( +".text\n" +".arm\n" +".global gen_insn_execbuf\n" +"gen_insn_execbuf:\n" +" nop\n" +" nop\n" +" nop\n" /* original instruction */ +" nop\n" +" ldr pc, [pc, #4]\n" /* ssbreak */ +" nop\n" /* retbreak */ +" nop\n" +" nop\n" /* stored PC-4(next insn addr) */ + +".global pc_dep_insn_execbuf\n" +"pc_dep_insn_execbuf:\n" +" str r0, [sp, #-4]\n" +" ldr r0, [pc, #12]\n" +" nop\n" /* instruction with replaced PC */ +" ldr r0, [sp, #-4]\n" +" ldr pc, [pc, #4]\n" /* ssbreak */ +" nop\n" /* retbreak */ +" nop\n" /* stored PC */ +" nop\n" /* stored PC-4 (next insn addr) */ + +".global b_r_insn_execbuf\n" +"b_r_insn_execbuf:\n" +" nop\n" /* bx, blx (Rm) */ +" ldr pc, np1\n" +" nop\n" +" nop\n" +" nop\n" +" nop\n" /* retbreak */ +" nop\n" +"np1:\n" +" nop\n" /* stored PC-4 (next insn addr) */ + +".global b_cond_insn_execbuf\n" +"b_cond_insn_execbuf:\n" +" beq condway\n" +" ldr pc, np2\n" +"condway:\n" +" ldr pc, bd2\n" +" nop\n" +" nop\n" +" nop\n" /* retbreak */ +"bd2:\n" +" nop\n" /* branch displacement */ +"np2:\n" +" nop\n" /* stored PC-4 (next insn addr) */ + +".global blx_off_insn_execbuf\n" +"blx_off_insn_execbuf:\n" +" ldreq lr, bd3\n" +" blxeq lr\n" +" ldr pc, np3\n" +" nop\n" +" nop\n" +" nop\n" /* retbreak */ +"bd3:\n" +" nop\n" /* branch displacement */ +"np3:\n" +" nop\n" /* stored PC-4 (next insn addr) */ +); diff --git a/kprobe/arch/arm/swap-asm/trampoline_arm.h b/arch/arm/probes/tramps_arm.h similarity index 86% rename from kprobe/arch/arm/swap-asm/trampoline_arm.h rename to arch/arm/probes/tramps_arm.h index 0bbf930..6989b8b 100644 --- a/kprobe/arch/arm/swap-asm/trampoline_arm.h +++ b/arch/arm/probes/tramps_arm.h @@ -1,5 +1,4 @@ /** - * @file kprobe/arch/asm-arm/trampoline_arm.h * @author Ekaterina Gorelkina : initial implementation for ARM/MIPS * @author Alexey Gerenkov User-Space * Probes initial implementation; @@ -29,13 +28,11 @@ * * Copyright (C) Samsung Electronics, 2006-2010 * - * @section DESCRIPTION - * - * Provides intefrace for trampoline_arm.S */ -#ifndef __ASM_ARM_TRAMPOLINE_ARM_H -#define __ASM_ARM_TRAMPOLINE_ARM_H +#ifndef _SWAP_ASM_TRAMPS_ARM_H +#define _SWAP_ASM_TRAMPS_ARM_H + void gen_insn_execbuf(void); void pc_dep_insn_execbuf(void); @@ -43,4 +40,5 @@ void b_r_insn_execbuf(void); void b_cond_insn_execbuf(void); void blx_off_insn_execbuf(void); -#endif /* __ASM_ARM_TRAMPOLINE_ARM_H */ + +#endif /* _SWAP_ASM_TRAMPS_ARM_H */ diff --git a/arch/arm/probes/tramps_thumb.c b/arch/arm/probes/tramps_thumb.c new file mode 100644 index 0000000..c9ce11d --- /dev/null +++ b/arch/arm/probes/tramps_thumb.c @@ -0,0 +1,169 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2016 + * + * 2016 Vyacheslav Cherkashin + * + */ + + +__asm( +".text\n" +".thumb\n" + +".global gen_insn_execbuf_thumb\n" +"gen_insn_execbuf_thumb:\n" + "nop\n" + "nop\n" + "nop\n" /* original instruction */ + "nop\n" /* original instruction */ + "nop\n" + "nop\n" + "nop\n" + "sub sp, sp, #8\n" + "str r0, [sp, #0]\n" + "ldr r0, [pc, #12]\n" + "str r0, [sp, #4]\n" + "nop\n" + "pop {r0, pc}\n" /* ssbreak */ + "nop\n" /* retbreak */ + "nop\n" + "nop\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + + "nop\n" + +".global pc_dep_insn_execbuf_thumb\n" +".align 4\n" +"pc_dep_insn_execbuf_thumb:\n" + "push {r6, r7}\n" + "ldr r6, i1\n" + "mov r7, sp\n" + "mov sp, r6\n" + "nop\n" /* PC -> SP */ + "nop\n" /* PC -> SP */ + "mov sp, r7\n" + "pop {r6, r7}\n" + "push {r0, r1}\n" + "ldr r0, i2\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0, pc}\n" /* ssbreak */ + "nop\n" /* retbreak */ +"i1:\n" + "nop\n" /* stored PC hi */ + "nop\n" /* stored PC lo */ +"i2:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global b_r_insn_execbuf_thumb\n" +".align 4\n" +"b_r_insn_execbuf_thumb:\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" /* bx,blx (Rm) */ + "nop\n" + "push {r0,r1}\n" + "ldr r0, np\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ + "nop\n" + "nop\n" +"np:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global b_off_insn_execbuf_thumb\n" +".align 4\n" +"b_off_insn_execbuf_thumb:\n" + "push {r0,r1}\n" + "ldr r0, bd\n" + "str r0, [sp, #4]\n" + "pop {r0, pc}\n" + "nop\n" + "nop\n" + "push {r0,r1}\n" + "ldr r0, np2\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ +"bd:\n" + "nop\n" /* branch displacement hi */ + "nop\n" /* branch displacement lo */ +"np2:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global b_cond_insn_execbuf_thumb\n" +".align 4\n" +"b_cond_insn_execbuf_thumb:\n" + "beq condway\n" + "push {r0,r1}\n" + "ldr r0, np4\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" +"condway:\n" + "push {r0,r1}\n" + "ldr r0, bd4\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ +"bd4:\n" + "nop\n" /* branch displacement hi */ + "nop\n" /* branch displacement lo */ +"np4:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global cbz_insn_execbuf_thumb\n" +".align 4\n" +"cbz_insn_execbuf_thumb:\n" + "nop\n" /* cbz */ + "push {r0,r1}\n" + "ldr r0, np5\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "push {r0,r1}\n" + "ldr r0, bd5\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ +"bd5:\n" + "nop\n" /* branch displacement hi */ + "nop\n" /* branch displacement lo */ +"np5:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ +); diff --git a/uprobe/arch/arm/swap-asm/trampoline_thumb.h b/arch/arm/probes/tramps_thumb.h similarity index 86% rename from uprobe/arch/arm/swap-asm/trampoline_thumb.h rename to arch/arm/probes/tramps_thumb.h index c67cbf8..5503890 100644 --- a/uprobe/arch/arm/swap-asm/trampoline_thumb.h +++ b/arch/arm/probes/tramps_thumb.h @@ -1,5 +1,4 @@ /** - * @file uprobe/arch/asm-arm/trampoline_thumb.h * @author Alexey Gerenkov User-Space Probes initial * implementation; Support x86/ARM/MIPS for both user and kernel spaces. * @author Ekaterina Gorelkina : redesign module for @@ -25,14 +24,12 @@ * * Copyright (C) Samsung Electronics, 2006-2010 * - * @section DESCRIPTION - * - * Thumb trampolines. */ -#ifndef __ASM_ARM_TRAMPOLINE_THUMB_H -#define __ASM_ARM_TRAMPOLINE_THUMB_H +#ifndef _SWAP_ASM_TRAMPS_THUMB_H +#define _SWAP_ASM_TRAMPS_THUMB_H + void gen_insn_execbuf_thumb(void); void pc_dep_insn_execbuf_thumb(void); @@ -41,4 +38,5 @@ void b_off_insn_execbuf_thumb(void); void b_cond_insn_execbuf_thumb(void); void cbz_insn_execbuf_thumb(void); -#endif /* __ASM_ARM_TRAMPOLINE_THUMB_H */ + +#endif /* _SWAP_ASM_TRAMPS_THUMB_H */ diff --git a/kprobe/Kbuild b/kprobe/Kbuild index 9403c42..7af581e 100644 --- a/kprobe/Kbuild +++ b/kprobe/Kbuild @@ -8,8 +8,11 @@ swap_kprobe-y := swap_kprobes.o \ swap_ktd.o ### ARM -swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/swap_kprobes.o \ - arch/arm/swap-asm/trampoline_arm.o +swap_kprobe-$(CONFIG_ARM) += \ + arch/arm/swap-asm/swap_kprobes.o \ + ../arch/arm/probes/probes_arm.o \ + ../arch/arm/probes/tramps_arm.o + ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/memory_rwx.o endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) diff --git a/kprobe/arch/arm/swap-asm/swap_kprobes.c b/kprobe/arch/arm/swap-asm/swap_kprobes.c index f4e004c..7f438c4 100644 --- a/kprobe/arch/arm/swap-asm/swap_kprobes.c +++ b/kprobe/arch/arm/swap-asm/swap_kprobes.c @@ -35,274 +35,23 @@ */ #include -#include - -#include "swap_kprobes.h" -#include "trampoline_arm.h" -#include - -#include -#include -#include -#include - #include #include #include #include #include - - -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) +#include +#include +#include +#include +#include +#include +#include "swap_kprobes.h" static void (*__swap_register_undef_hook)(struct undef_hook *hook); static void (*__swap_unregister_undef_hook)(struct undef_hook *hook); -static unsigned long get_addr_b(unsigned long insn, unsigned long addr) -{ - /* real position less then PC by 8 */ - return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn)); -} - -static int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, - kprobe_opcode_t insn, int uregs) -{ - int i; - - if (uregs & 0x10) { - int reg_mask = 0x1; - /* search in reg list */ - for (i = 0; i < 13; i++, reg_mask <<= 1) { - if (!(insn & reg_mask)) - break; - } - } else { - for (i = 0; i < 13; i++) { - if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i)) - continue; - if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i)) - continue; - if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i)) - continue; - if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i)) - continue; - break; - } - } - - if (i == 13) { - DBPRINTF("there are no free register %x in insn %lx!", - uregs, insn); - return -EINVAL; - } - DBPRINTF("prep_pc_dep_insn_execbuf: using R%d, changing regs %x", - i, uregs); - - /* set register to save */ - ARM_INSN_REG_SET_RD(insns[0], i); - /* set register to load address to */ - ARM_INSN_REG_SET_RD(insns[1], i); - /* set instruction to execute and patch it */ - if (uregs & 0x10) { - ARM_INSN_REG_CLEAR_MR(insn, 15); - ARM_INSN_REG_SET_MR(insn, i); - } else { - if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15)) - ARM_INSN_REG_SET_RN(insn, i); - if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15)) - ARM_INSN_REG_SET_RD(insn, i); - if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15)) - ARM_INSN_REG_SET_RS(insn, i); - if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15)) - ARM_INSN_REG_SET_RM(insn, i); - } - - insns[UPROBES_TRAMP_INSN_IDX] = insn; - /* set register to restore */ - ARM_INSN_REG_SET_RD(insns[3], i); - - return 0; -} - -static int arch_check_insn_arm(unsigned long insn) -{ - /* check instructions that can change PC by nature */ - if ( - /* ARM_INSN_MATCH(UNDEF, insn) || */ - ARM_INSN_MATCH(AUNDEF, insn) || - ARM_INSN_MATCH(SWI, insn) || - ARM_INSN_MATCH(BREAK, insn) || - ARM_INSN_MATCH(BXJ, insn)) { - goto bad_insn; -#ifndef CONFIG_CPU_V7 - /* check instructions that can write result to PC */ - } else if ((ARM_INSN_MATCH(DPIS, insn) || - ARM_INSN_MATCH(DPRS, insn) || - ARM_INSN_MATCH(DPI, insn) || - ARM_INSN_MATCH(LIO, insn) || - ARM_INSN_MATCH(LRO, insn)) && - (ARM_INSN_REG_RD(insn) == 15)) { - goto bad_insn; -#endif /* CONFIG_CPU_V7 */ - /* check special instruction loads store multiple registers */ - } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) && - /* store PC or load to PC */ - (ARM_INSN_REG_MR(insn, 15) || - /* store/load with PC update */ - ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) { - goto bad_insn; - } - - return 0; - -bad_insn: - return -EFAULT; -} - -static int make_branch_tarmpoline(unsigned long addr, unsigned long insn, - unsigned long *tramp) -{ - int ok = 0; - - /* B */ - if (ARM_INSN_MATCH(B, insn) && - !ARM_INSN_MATCH(BLX1, insn)) { - /* B check can be false positive on BLX1 instruction */ - memcpy(tramp, b_cond_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[0] |= insn & 0xf0000000; - tramp[6] = get_addr_b(insn, addr); - tramp[7] = addr + 4; - ok = 1; - /* BX, BLX (Rm) */ - } else if (ARM_INSN_MATCH(BX, insn) || - ARM_INSN_MATCH(BLX2, insn)) { - memcpy(tramp, b_r_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[0] = insn; - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[7] = addr + 4; - ok = 1; - /* BL, BLX (Off) */ - } else if (ARM_INSN_MATCH(BLX1, insn)) { - memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[0] |= 0xe0000000; - tramp[1] |= 0xe0000000; - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[6] = get_addr_b(insn, addr) + - 2 * (insn & 01000000) + 1; /* jump to thumb */ - tramp[7] = addr + 4; - ok = 1; - /* BL */ - } else if (ARM_INSN_MATCH(BL, insn)) { - memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[0] |= insn & 0xf0000000; - tramp[1] |= insn & 0xf0000000; - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[6] = get_addr_b(insn, addr); - tramp[7] = addr + 4; - ok = 1; - } - - return ok; -} - -/** - * @brief Creates ARM trampoline. - * - * @param addr Probe address. - * @param insn Instuction at this address. - * @param tramp Pointer to memory for trampoline. - * @return 0 on success, error code on error. - */ -int arch_make_trampoline_arm(unsigned long addr, unsigned long insn, - unsigned long *tramp) -{ - int ret, uregs, pc_dep; - - if (addr & 0x03) { - printk(KERN_INFO "Error in %s at %d: attempt to register uprobe " - "at an unaligned address\n", __FILE__, __LINE__); - return -EINVAL; - } - - ret = arch_check_insn_arm(insn); - if (ret) - return ret; - - if (make_branch_tarmpoline(addr, insn, tramp)) - return 0; - - uregs = pc_dep = 0; - /* Rm */ - if (ARM_INSN_MATCH(CLZ, insn)) { - uregs = 0xa; - if (ARM_INSN_REG_RM(insn) == 15) - pc_dep = 1; - /* Rn, Rm ,Rd */ - } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) || - ARM_INSN_MATCH(SRO, insn)) { - uregs = 0xb; - if ((ARM_INSN_REG_RN(insn) == 15) || - (ARM_INSN_REG_RM(insn) == 15) || - (ARM_INSN_MATCH(SRO, insn) && - (ARM_INSN_REG_RD(insn) == 15))) { - pc_dep = 1; - } - /* Rn ,Rd */ - } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) || - ARM_INSN_MATCH(SIO, insn)) { - uregs = 0x3; - if ((ARM_INSN_REG_RN(insn) == 15) || - (ARM_INSN_MATCH(SIO, insn) && - (ARM_INSN_REG_RD(insn) == 15))) { - pc_dep = 1; - } - /* Rn, Rm, Rs */ - } else if (ARM_INSN_MATCH(DPRS, insn)) { - uregs = 0xd; - if ((ARM_INSN_REG_RN(insn) == 15) || - (ARM_INSN_REG_RM(insn) == 15) || - (ARM_INSN_REG_RS(insn) == 15)) { - pc_dep = 1; - } - /* register list */ - } else if (ARM_INSN_MATCH(SM, insn)) { - uregs = 0x10; - if (ARM_INSN_REG_MR(insn, 15)) - pc_dep = 1; - } - - /* check instructions that can write result to SP and uses PC */ - if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) { - printk(KERN_INFO "Error in %s at %d: instruction check failed (arm)\n", - __FILE__, __LINE__); - return -EFAULT; - } - - if (unlikely(uregs && pc_dep)) { - memcpy(tramp, pc_dep_insn_execbuf, KPROBES_TRAMP_LEN); - if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) { - printk(KERN_INFO "Error in %s at %d: failed " - "to prepare exec buffer for insn %lx!", - __FILE__, __LINE__, insn); - return -EINVAL; - } - - tramp[6] = addr + 8; - } else { - memcpy(tramp, gen_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[KPROBES_TRAMP_INSN_IDX] = insn; - } - - /* TODO: remove for kprobe */ - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[7] = addr + 4; - - return 0; -} -EXPORT_SYMBOL_GPL(arch_make_trampoline_arm); /** * @brief Creates trampoline for kprobe. @@ -321,7 +70,7 @@ int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm) return -ENOMEM; p->opcode = *(unsigned long *)p->addr; - ret = arch_make_trampoline_arm(p->addr, p->opcode, tramp); + ret = make_trampoline_arm(p->addr, p->opcode, tramp); if (ret) { swap_slot_free(sm, tramp); return ret; @@ -781,7 +530,13 @@ void swap_arch_exit_kprobes(void) swap_unregister_undef_hook(&undef_ho_k); } -/* export symbol for trampoline_arm.h */ + +/* export symbol for probes_arm.h */ +EXPORT_SYMBOL_GPL(noret_arm); +EXPORT_SYMBOL_GPL(make_trampoline_arm); + +#include +/* export symbol for tramps_arm.h */ EXPORT_SYMBOL_GPL(gen_insn_execbuf); EXPORT_SYMBOL_GPL(pc_dep_insn_execbuf); EXPORT_SYMBOL_GPL(b_r_insn_execbuf); diff --git a/kprobe/arch/arm/swap-asm/swap_kprobes.h b/kprobe/arch/arm/swap-asm/swap_kprobes.h index 9f16dbf..90945ba 100644 --- a/kprobe/arch/arm/swap-asm/swap_kprobes.h +++ b/kprobe/arch/arm/swap-asm/swap_kprobes.h @@ -236,378 +236,6 @@ static inline void swap_set_arg(struct pt_regs *regs, int num, regs->uregs[num] = val; } -/* undefined */ -#define MASK_ARM_INSN_UNDEF 0x0FF00000 -#define PTRN_ARM_INSN_UNDEF 0x03000000 - -#define MASK_THUMB_INSN_UNDEF 0xFE00 -#define PTRN_THUMB_INSN_UNDEF 0xDE00 - -/* architecturally undefined */ -#define MASK_ARM_INSN_AUNDEF 0x0FF000F0 -#define PTRN_ARM_INSN_AUNDEF 0x07F000F0 - -/* branches */ -#define MASK_ARM_INSN_B 0x0F000000 -#define PTRN_ARM_INSN_B 0x0A000000 - -#define MASK_THUMB_INSN_B1 0xF000 -#define PTRN_THUMB_INSN_B1 0xD000 /* b label */ - -#define MASK_THUMB_INSN_B2 0xF800 -#define PTRN_THUMB_INSN_B2 0xE000 /* b label */ - -#define MASK_THUMB_INSN_CBZ 0xF500 -#define PTRN_THUMB_INSN_CBZ 0xB100 /* CBZ/CBNZ */ - -#define MASK_THUMB2_INSN_B1 0xD000F800 -#define PTRN_THUMB2_INSN_B1 0x8000F000 - -#define MASK_THUMB2_INSN_B2 0xD000F800 -#define PTRN_THUMB2_INSN_B2 0x9000F000 - -#define MASK_ARM_INSN_BL 0x0F000000 -#define PTRN_ARM_INSN_BL 0x0B000000 - -/* #define MASK_THUMB_INSN_BL 0xF800 */ -/* #define PTRN_THUMB_INSN_BL 0xF000 shared between BL and BLX */ -/* #define PTRN_THUMB_INSN_BL 0xF800 */ - -#define MASK_THUMB2_INSN_BL 0xD000F800 -#define PTRN_THUMB2_INSN_BL 0xD000F000 /* bl imm swapped */ - -#define MASK_ARM_INSN_BLX1 0xFE000000 -#define PTRN_ARM_INSN_BLX1 0xFA000000 - -/* #define MASK_THUMB_INSN_BLX1 0xF800 */ -/* #define PTRN_THUMB_INSN_BLX1 0xF000 */ - -#define MASK_THUMB2_INSN_BLX1 0xD001F800 -#define PTRN_THUMB2_INSN_BLX1 0xC000F000 - -#define MASK_ARM_INSN_BLX2 0x0FF000F0 -#define PTRN_ARM_INSN_BLX2 0x01200030 - -#define MASK_THUMB_INSN_BLX2 0xFF80 /* blx reg */ -#define PTRN_THUMB_INSN_BLX2 0x4780 - -#define MASK_ARM_INSN_BX 0x0FF000F0 -#define PTRN_ARM_INSN_BX 0x01200010 - -#define MASK_THUMB_INSN_BX 0xFF80 -#define PTRN_THUMB_INSN_BX 0x4700 - -#define MASK_ARM_INSN_BXJ 0x0FF000F0 -#define PTRN_ARM_INSN_BXJ 0x01200020 - -#define MASK_THUMB2_INSN_BXJ 0xD000FFF0 -#define PTRN_THUMB2_INSN_BXJ 0x8000F3C0 - - -/* software interrupts */ -#define MASK_ARM_INSN_SWI 0x0F000000 -#define PTRN_ARM_INSN_SWI 0x0F000000 - -#define MASK_THUMB_INSN_SWI 0xFF00 -#define PTRN_THUMB_INSN_SWI 0xDF00 - -/* break */ -#define MASK_ARM_INSN_BREAK 0xFFF000F0 -#define PTRN_ARM_INSN_BREAK 0xE1200070 -/* A8-56 ARM DDI 046B if cond != ‘1110’ then UNPREDICTABLE; */ - -#define MASK_THUMB_INSN_BREAK 0xFF00 -#define PTRN_THUMB_INSN_BREAK 0xBE00 - -/* CLZ */ -#define MASK_ARM_INSN_CLZ 0x0FFF0FF0 -#define PTRN_ARM_INSN_CLZ 0x016F0F10 - -/* Data processing immediate shift */ -#define MASK_ARM_INSN_DPIS 0x0E000010 -#define PTRN_ARM_INSN_DPIS 0x00000000 -/* Data processing register shift */ -#define MASK_ARM_INSN_DPRS 0x0E000090 -#define PTRN_ARM_INSN_DPRS 0x00000010 - -#define MASK_THUMB2_INSN_DPRS 0xFFE00000 -#define PTRN_THUMB2_INSN_DPRS 0xEA000000 - -/* Data processing immediate */ -#define MASK_ARM_INSN_DPI 0x0E000000 -#define PTRN_ARM_INSN_DPI 0x02000000 - -#define MASK_THUMB_INSN_DP 0xFC00 -#define PTRN_THUMB_INSN_DP 0x4000 - -#define MASK_THUMB_INSN_APC 0xF800 -#define PTRN_THUMB_INSN_APC 0xA000 /* ADD Rd, [PC, # * 4] */ - -#define MASK_THUMB2_INSN_DPI 0xFBE08000 -/* #define PTRN_THUMB2_INSN_DPI 0xF0000000 */ -/* A6-19 ARM DDI 0406B */ -#define PTRN_THUMB2_INSN_DPI 0xF2000000 -/* A6-19 ARM DDI 0406B */ - -#define MASK_THUMB_INSN_MOV3 0xFF00 -#define PTRN_THUMB_INSN_MOV3 0x4600 /* MOV Rd, PC */ - -#define MASK_THUMB2_INSN_RSBW 0x8000fbe0 -#define PTRN_THUMB2_INSN_RSBW 0x0000f1c0 /* RSB{S}.W Rd,Rn,# */ - -#define MASK_THUMB2_INSN_RORW 0xf0f0ffe0 -#define PTRN_THUMB2_INSN_RORW 0xf000fa60 /* ROR{S}.W Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_ROR 0x0030ffef -#define PTRN_THUMB2_INSN_ROR 0x0030ea4f /* ROR{S} Rd, Rm, # */ - -#define MASK_THUMB2_INSN_LSLW1 0xf0f0ffe0 -#define PTRN_THUMB2_INSN_LSLW1 0xf000fa00 /* LSL{S}.W Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_LSLW2 0x0030ffef -#define PTRN_THUMB2_INSN_LSLW2 0x0000ea4f /* LSL{S}.W Rd, Rm, #*/ - -#define MASK_THUMB2_INSN_LSRW1 0xf0f0ffe0 -#define PTRN_THUMB2_INSN_LSRW1 0xf000fa20 /* LSR{S}.W Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_LSRW2 0x0030ffef -#define PTRN_THUMB2_INSN_LSRW2 0x0010ea4f /* LSR{S}.W Rd, Rm, # */ - -#define MASK_THUMB2_INSN_TEQ1 0x8f00fbf0 -#define PTRN_THUMB2_INSN_TEQ1 0x0f00f090 /* TEQ Rn, # */ - -#define MASK_THUMB2_INSN_TEQ2 0x0f00fff0 -#define PTRN_THUMB2_INSN_TEQ2 0x0f00ea90 /* TEQ Rn, Rm{,} */ - -#define MASK_THUMB2_INSN_TST1 0x8f00fbf0 -#define PTRN_THUMB2_INSN_TST1 0x0f00f010 /* TST Rn, # */ - -#define MASK_THUMB2_INSN_TST2 0x0f00fff0 -#define PTRN_THUMB2_INSN_TST2 0x0f00ea10 /* TST Rn, Rm{,} */ - - -/* Load immediate offset */ -#define MASK_ARM_INSN_LIO 0x0E100000 -#define PTRN_ARM_INSN_LIO 0x04100000 - -#define MASK_THUMB_INSN_LIO1 0xF800 -#define PTRN_THUMB_INSN_LIO1 0x6800 /* LDR */ - -#define MASK_THUMB_INSN_LIO2 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_LIO2 0x7800 /* LDRB */ - -#define MASK_THUMB_INSN_LIO3 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_LIO3 0x8800 /* LDRH */ - -#define MASK_THUMB_INSN_LIO4 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_LIO4 0x9800 /* LDR SP relative */ - -#define MASK_THUMB2_INSN_LDRW 0x0000fff0 -#define PTRN_THUMB2_INSN_LDRW 0x0000f850 /* LDR.W Rt, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRW1 MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRW1 0x0000f8d0 /* LDR.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRBW MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRBW 0x0000f810 /* LDRB.W Rt, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRBW1 MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRBW1 0x0000f890 /* LDRB.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRHW MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRHW 0x0000f830 /* LDRH.W Rt, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRHW1 MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRHW1 0x0000f8b0 /* LDRH.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRD 0x0000fed0 -#define PTRN_THUMB2_INSN_LDRD 0x0000e850 /* LDRD Rt, Rt2, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRD1 MASK_THUMB2_INSN_LDRD -#define PTRN_THUMB2_INSN_LDRD1 0x0000e8d0 /* LDRD Rt, Rt2, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRWL 0x0fc0fff0 -#define PTRN_THUMB2_INSN_LDRWL 0x0000f850 /* LDR.W Rt, [Rn,Rm,LSL #] */ - -#define MASK_THUMB2_INSN_LDREX 0x0f00ffff -#define PTRN_THUMB2_INSN_LDREX 0x0f00e85f /* LDREX Rt, [PC, #] */ - -#define MASK_THUMB2_INSN_MUL 0xf0f0fff0 -#define PTRN_THUMB2_INSN_MUL 0xf000fb00 /* MUL Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_DP 0x0000ff00 -#define PTRN_THUMB2_INSN_DP 0x0000eb00 /* ADD/SUB/SBC/...Rd,Rn,Rm{,} */ - - - - -/* Store immediate offset */ -#define MASK_ARM_INSN_SIO MASK_ARM_INSN_LIO -#define PTRN_ARM_INSN_SIO 0x04000000 - -#define MASK_THUMB_INSN_SIO1 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO1 0x6000 /* STR */ - -#define MASK_THUMB_INSN_SIO2 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO2 0x7000 /* STRB */ - -#define MASK_THUMB_INSN_SIO3 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO3 0x8000 /* STRH */ - -#define MASK_THUMB_INSN_SIO4 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO4 0x9000 /* STR SP relative */ - -#define MASK_THUMB2_INSN_STRW 0x0fc0fff0 -#define PTRN_THUMB2_INSN_STRW 0x0000f840 /* STR.W Rt,[Rn,Rm,{LSL #}] */ - -#define MASK_THUMB2_INSN_STRW1 0x0000fff0 -#define PTRN_THUMB2_INSN_STRW1 0x0000f8c0 /* STR.W Rt, [Rn, #imm12] - * STR.W Rt, [PC, #imm12] shall be - * skipped, because it hangs - * on Tegra. WTF */ - -#define MASK_THUMB2_INSN_STRHW MASK_THUMB2_INSN_STRW -#define PTRN_THUMB2_INSN_STRHW 0x0000f820 /* STRH.W Rt,[Rn,Rm,{LSL #}] */ - -#define MASK_THUMB2_INSN_STRHW1 0x0000fff0 -#define PTRN_THUMB2_INSN_STRHW1 0x0000f8a0 /* STRH.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_STRHT 0x0f00fff0 /* strht r1, [pc, #imm] illegal - * instruction on Tegra. WTF */ -#define PTRN_THUMB2_INSN_STRHT 0x0e00f820 /* STRHT Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_STRT 0x0f00fff0 -#define PTRN_THUMB2_INSN_STRT 0x0e00f840 /* STRT Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_STRBW MASK_THUMB2_INSN_STRW -#define PTRN_THUMB2_INSN_STRBW 0x0000f800 /* STRB.W Rt,[Rn,Rm,{LSL #}] */ - -#define MASK_THUMB2_INSN_STRBW1 0x0000fff0 -#define PTRN_THUMB2_INSN_STRBW1 0x0000f880 /* STRB.W Rt, [Rn, #] - * STRB.W Rt, [PC, #imm12] shall be - * skipped, because it hangs - * on Tegra. WTF */ - -#define MASK_THUMB2_INSN_STRBT 0x0f00fff0 -#define PTRN_THUMB2_INSN_STRBT 0x0e00f800 /* STRBT Rt, [Rn, #}] */ - -#define MASK_THUMB2_INSN_STRD 0x0000fe50 -/* STR{D,EX,EXB,EXH,EXD} Rt, Rt2, [Rn, #] */ -#define PTRN_THUMB2_INSN_STRD 0x0000e840 - - -/* Load register offset */ -#define MASK_ARM_INSN_LRO 0x0E100010 -#define PTRN_ARM_INSN_LRO 0x06100000 - -#define MASK_THUMB_INSN_LRO1 0xFE00 -#define PTRN_THUMB_INSN_LRO1 0x5600 /* LDRSB */ - -#define MASK_THUMB_INSN_LRO2 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO2 0x5800 /* LDR */ - -#define MASK_THUMB_INSN_LRO3 0xf800 -#define PTRN_THUMB_INSN_LRO3 0x4800 /* LDR Rd, [PC, # * 4] */ - -#define MASK_THUMB_INSN_LRO4 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO4 0x5A00 /* LDRH */ - -#define MASK_THUMB_INSN_LRO5 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO5 0x5C00 /* LDRB */ - -#define MASK_THUMB_INSN_LRO6 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO6 0x5E00 /* LDRSH */ - -#define MASK_THUMB2_INSN_ADR 0x8000fa1f -#define PTRN_THUMB2_INSN_ADR 0x0000f20f - - - -/* Store register offset */ -#define MASK_ARM_INSN_SRO MASK_ARM_INSN_LRO -#define PTRN_ARM_INSN_SRO 0x06000000 - -#define MASK_THUMB_INSN_SRO1 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_SRO1 0x5000 /* STR */ - -#define MASK_THUMB_INSN_SRO2 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_SRO2 0x5200 /* STRH */ - -#define MASK_THUMB_INSN_SRO3 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_SRO3 0x5400 /* STRB */ - -/* Load multiple */ -#define MASK_ARM_INSN_LM 0x0E100000 -#define PTRN_ARM_INSN_LM 0x08100000 - -#define MASK_THUMB2_INSN_LDMIA 0x8000ffd0 -#define PTRN_THUMB2_INSN_LDMIA 0x8000e890 /* LDMIA(.W) Rn(!),{Rx-PC} */ - -#define MASK_THUMB2_INSN_LDMDB 0x8000ffd0 -#define PTRN_THUMB2_INSN_LDMDB 0x8000e910 /* LDMDB(.W) Rn(!), {Rx-PC} */ - -/* Store multiple */ -#define MASK_ARM_INSN_SM MASK_ARM_INSN_LM -#define PTRN_ARM_INSN_SM 0x08000000 - - -/* Coprocessor load/store and double register transfers */ -#define MASK_ARM_INSN_CLS 0x0E000000 -#define PTRN_ARM_INSN_CLS 0x0C000000 -/* Coprocessor register transfers */ -#define MASK_ARM_INSN_CRT 0x0F000010 -#define PTRN_ARM_INSN_CRT 0x0E000010 - -#define ARM_INSN_MATCH(name, insn) \ - ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name) -#define THUMB_INSN_MATCH(name, insn) \ - (((insn & 0x0000FFFF) & MASK_THUMB_INSN_##name) == \ - PTRN_THUMB_INSN_##name) -#define THUMB2_INSN_MATCH(name, insn) \ - ((insn & MASK_THUMB2_INSN_##name) == PTRN_THUMB2_INSN_##name) - -#define ARM_INSN_REG_RN(insn) \ - ((insn & 0x000F0000)>>16) - -#define ARM_INSN_REG_SET_RN(insn, nreg) \ - { insn &= ~0x000F0000; insn |= nreg<<16; } - -#define ARM_INSN_REG_RD(insn) \ - ((insn & 0x0000F000)>>12) - -#define ARM_INSN_REG_SET_RD(insn, nreg) \ - { insn &= ~0x0000F000; insn |= nreg<<12; } - -#define ARM_INSN_REG_RS(insn) \ - ((insn & 0x00000F00)>>8) - -#define ARM_INSN_REG_SET_RS(insn, nreg) \ - { insn &= ~0x00000F00; insn |= nreg<<8; } - -#define ARM_INSN_REG_RM(insn) \ - (insn & 0x0000000F) - -#define ARM_INSN_REG_SET_RM(insn, nreg) \ - { insn &= ~0x0000000F; insn |= nreg; } - -#define ARM_INSN_REG_MR(insn, nreg) \ - (insn & (1 << nreg)) - -#define ARM_INSN_REG_SET_MR(insn, nreg) \ - { insn |= (1 << nreg); } - -#define ARM_INSN_REG_CLEAR_MR(insn, nreg) \ - { insn &= ~(1 << nreg); } - -#define THUMB2_INSN_REG_RT(insn) ((insn & 0xf0000000) >> 28) -#define THUMB2_INSN_REG_RT2(insn) ((insn & 0x0f000000) >> 24) -#define THUMB2_INSN_REG_RN(insn) (insn & 0x0000000f) -#define THUMB2_INSN_REG_RD(insn) ((insn & 0x0f000000) >> 24) -#define THUMB2_INSN_REG_RM(insn) ((insn & 0x000f0000) >> 16) - - - - /** * @struct kp_core_ctlblk * @brief Per-cpu kp_core control block. @@ -642,9 +270,6 @@ void swap_unregister_undef_hook(struct undef_hook *hook); int arch_init_module_deps(void); -int arch_make_trampoline_arm(unsigned long addr, unsigned long insn, - unsigned long *tramp); - struct slot_manager; struct kretprobe; struct kretprobe_instance; diff --git a/kprobe/arch/arm/swap-asm/trampoline_arm.S b/kprobe/arch/arm/swap-asm/trampoline_arm.S deleted file mode 100644 index 8e26146..0000000 --- a/kprobe/arch/arm/swap-asm/trampoline_arm.S +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Function return probe trampoline: - * - init_kprobes() establishes a probepoint here - * - When the probed function returns, this probe - * causes the handlers to fire - */ - - .global gen_insn_execbuf -gen_insn_execbuf: - nop - nop - nop //original instruction - nop - ldr pc, [pc, #4] //ssbreak - nop //retbreak - nop - nop //stored PC-4(next insn addr) - - - - .global pc_dep_insn_execbuf -pc_dep_insn_execbuf: - str r0, [sp, #-4] - ldr r0, [pc, #12] - nop // instruction with replaced PC - ldr r0, [sp, #-4] - ldr pc, [pc, #4] //ssbreak - nop // retbreak - nop // stored PC - nop // stored PC-4 (next insn addr) - - .global b_r_insn_execbuf -b_r_insn_execbuf: - nop // bx, blx (Rm) - ldr pc, np1 - nop - nop - nop - nop // retbreak - nop -np1: nop // stored PC-4 (next insn addr) - - .global b_cond_insn_execbuf -b_cond_insn_execbuf: - beq condway - ldr pc, np2 -condway: ldr pc, bd2 - nop - nop - nop // retbreak -bd2: nop // branch displacement -np2: nop // stored PC-4 (next insn addr) - - .global blx_off_insn_execbuf -blx_off_insn_execbuf: - ldreq lr, bd3 - blxeq lr - ldr pc, np3 - nop - nop - nop // retbreak -bd3: nop // branch displacement -np3: nop // stored PC-4 (next insn addr) diff --git a/uprobe/Kbuild b/uprobe/Kbuild index e4e96f0..08fb3dd 100644 --- a/uprobe/Kbuild +++ b/uprobe/Kbuild @@ -7,9 +7,9 @@ swap_uprobe-y := swap_uprobes.o ### ARM swap_uprobe-$(CONFIG_ARM) += \ arch/arm/swap-asm/swap_uprobes.o \ - arch/arm/swap-asm/trampoline_thumb.o \ - arch/arm/swap-asm/decode_thumb.o \ - arch/arm/swap-asm/thumb_tramps.o + ../arch/arm/probes/probes_thumb.o \ + ../arch/arm/probes/tramps_thumb.o \ + ../arch/arm/probes/decode_thumb.o ### ARM64 diff --git a/uprobe/arch/arm/swap-asm/swap_uprobes.c b/uprobe/arch/arm/swap-asm/swap_uprobes.c index 771906d..2b9be2e 100644 --- a/uprobe/arch/arm/swap-asm/swap_uprobes.c +++ b/uprobe/arch/arm/swap-asm/swap_uprobes.c @@ -38,15 +38,12 @@ #include #include -#include #include #include - +#include +#include #include -#include -#include "decode_thumb.h" #include "swap_uprobes.h" -#include "trampoline_thumb.h" #define UBP_ARM (BREAKPOINT_INSTRUCTION) @@ -60,516 +57,6 @@ flush_icache_range((unsigned long)(addr), \ (unsigned long)(addr) + (size)) -static inline long branch_t16_dest(uprobe_opcode_t insn, unsigned int insn_addr) -{ - long offset = insn & 0x3ff; - offset -= insn & 0x400; - return insn_addr + 4 + offset * 2; -} - -static inline long branch_cond_t16_dest(uprobe_opcode_t insn, - unsigned int insn_addr) -{ - long offset = insn & 0x7f; - offset -= insn & 0x80; - return insn_addr + 4 + offset * 2; -} - -static inline long branch_t32_dest(uprobe_opcode_t insn, unsigned int insn_addr) -{ - unsigned int poff = insn & 0x3ff; - unsigned int offset = (insn & 0x07fe0000) >> 17; - - poff -= (insn & 0x400); - - if (insn & (1 << 12)) - return insn_addr + 4 + (poff << 12) + offset * 4; - else - return (insn_addr + 4 + (poff << 12) + offset * 4) & ~3; -} - -static inline long cbz_t16_dest(uprobe_opcode_t insn, unsigned int insn_addr) -{ - unsigned int i = (insn & 0x200) >> 3; - unsigned int offset = (insn & 0xf8) >> 2; - return insn_addr + 4 + i + offset; -} - -/* is instruction Thumb2 and NOT a branch, etc... */ -static int is_thumb2(uprobe_opcode_t insn) -{ - return ((insn & 0xf800) == 0xe800 || - (insn & 0xf800) == 0xf000 || - (insn & 0xf800) == 0xf800); -} - -static int arch_check_insn_thumb(unsigned long insn) -{ - int ret = 0; - - /* check instructions that can change PC */ - if (THUMB_INSN_MATCH(UNDEF, insn) || - THUMB2_INSN_MATCH(BLX1, insn) || - THUMB2_INSN_MATCH(BL, insn) || - THUMB_INSN_MATCH(SWI, insn) || - THUMB_INSN_MATCH(BREAK, insn) || - THUMB2_INSN_MATCH(B1, insn) || - THUMB2_INSN_MATCH(B2, insn) || - THUMB2_INSN_MATCH(BXJ, insn) || - (THUMB2_INSN_MATCH(ADR, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRW1, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRHW, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRHW1, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRWL, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - THUMB2_INSN_MATCH(LDMIA, insn) || - THUMB2_INSN_MATCH(LDMDB, insn) || - (THUMB2_INSN_MATCH(DP, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(RSBW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(RORW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(ROR, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSLW1, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSLW2, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSRW1, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSRW2, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */ - (THUMB2_INSN_MATCH(STRW1, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRBW1, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRHW1, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRHW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(LDRBW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(LDRHW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */ - (THUMB2_INSN_MATCH(LDRD, insn) || THUMB2_INSN_MATCH(LDRD1, insn) || - THUMB2_INSN_MATCH(STRD, insn))) { - ret = -EFAULT; - } - - return ret; -} - -static int prep_pc_dep_insn_execbuf_thumb(uprobe_opcode_t *insns, - uprobe_opcode_t insn, int uregs) -{ - unsigned char mreg = 0; - unsigned char reg = 0; - - if (THUMB_INSN_MATCH(APC, insn) || - THUMB_INSN_MATCH(LRO3, insn)) { - reg = ((insn & 0xffff) & uregs) >> 8; - } else if (THUMB_INSN_MATCH(MOV3, insn)) { - if (((((unsigned char)insn) & 0xff) >> 3) == 15) - reg = (insn & 0xffff) & uregs; - else - return 0; - } else if (THUMB2_INSN_MATCH(ADR, insn)) { - reg = ((insn >> 16) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(LDRW, insn) || - THUMB2_INSN_MATCH(LDRW1, insn) || - THUMB2_INSN_MATCH(LDRHW, insn) || - THUMB2_INSN_MATCH(LDRHW1, insn) || - THUMB2_INSN_MATCH(LDRWL, insn)) { - reg = ((insn >> 16) & uregs) >> 12; - if (reg == 15) - return 0; - /* - * LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped - */ - } else if (THUMB2_INSN_MATCH(LDRBW, insn) || - THUMB2_INSN_MATCH(LDRBW1, insn) || - THUMB2_INSN_MATCH(LDREX, insn)) { - reg = ((insn >> 16) & uregs) >> 12; - } else if (THUMB2_INSN_MATCH(DP, insn)) { - reg = ((insn >> 16) & uregs) >> 12; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(RSBW, insn)) { - reg = ((insn >> 12) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(RORW, insn)) { - reg = ((insn >> 12) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(ROR, insn) || - THUMB2_INSN_MATCH(LSLW1, insn) || - THUMB2_INSN_MATCH(LSLW2, insn) || - THUMB2_INSN_MATCH(LSRW1, insn) || - THUMB2_INSN_MATCH(LSRW2, insn)) { - reg = ((insn >> 12) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(TEQ1, insn) || - THUMB2_INSN_MATCH(TST1, insn)) { - reg = 15; - } else if (THUMB2_INSN_MATCH(TEQ2, insn) || - THUMB2_INSN_MATCH(TST2, insn)) { - reg = THUMB2_INSN_REG_RM(insn); - } - - if ((THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn) || - THUMB2_INSN_MATCH(STRD, insn) || - THUMB2_INSN_MATCH(STRHT, insn) || - THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHW1, insn) || - THUMB2_INSN_MATCH(STRHW, insn)) && - THUMB2_INSN_REG_RT(insn) == 15) { - reg = THUMB2_INSN_REG_RT(insn); - } - - if (reg == 6 || reg == 7) { - *((unsigned short *)insns + 0) = - (*((unsigned short *)insns + 0) & 0x00ff) | - ((1 << mreg) | (1 << (mreg + 1))); - *((unsigned short *)insns + 1) = - (*((unsigned short *)insns + 1) & 0xf8ff) | (mreg << 8); - *((unsigned short *)insns + 2) = - (*((unsigned short *)insns + 2) & 0xfff8) | (mreg + 1); - *((unsigned short *)insns + 3) = - (*((unsigned short *)insns + 3) & 0xffc7) | (mreg << 3); - *((unsigned short *)insns + 7) = - (*((unsigned short *)insns + 7) & 0xf8ff) | (mreg << 8); - *((unsigned short *)insns + 8) = - (*((unsigned short *)insns + 8) & 0xffc7) | (mreg << 3); - *((unsigned short *)insns + 9) = - (*((unsigned short *)insns + 9) & 0xffc7) | - ((mreg + 1) << 3); - *((unsigned short *)insns + 10) = - (*((unsigned short *)insns + 10) & 0x00ff) | - ((1 << mreg) | (1 << (mreg + 1))); - } - - if (THUMB_INSN_MATCH(APC, insn)) { - /* ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4 */ - *((unsigned short *)insns + 4) = ((insn & 0xffff) | 0x800); - } else if (THUMB_INSN_MATCH(LRO3, insn)) { - /* LDR Rd, [PC, #immed_8*4] -> - * LDR Rd, [SP, #immed_8*4] */ - *((unsigned short *)insns + 4) = - ((insn & 0xffff) + 0x5000); - } else if (THUMB_INSN_MATCH(MOV3, insn)) { - /* MOV Rd, PC -> MOV Rd, SP */ - *((unsigned short *)insns + 4) = - ((insn & 0xffff) ^ 0x10); - } else if (THUMB2_INSN_MATCH(ADR, insn)) { - /* ADDW Rd,PC,#imm -> ADDW Rd,SP,#imm */ - insns[2] = (insn & 0xfffffff0) | 0x0d; - } else if (THUMB2_INSN_MATCH(LDRW, insn) || - THUMB2_INSN_MATCH(LDRBW, insn) || - THUMB2_INSN_MATCH(LDRHW, insn)) { - /* LDR.W Rt, [PC, #-] -> - * LDR.W Rt, [SP, #-] - * !!!!!!!!!!!!!!!!!!!!!!!! - * !!! imm_12 vs. imm_8 !!! - * !!!!!!!!!!!!!!!!!!!!!!!! */ - insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; - } else if (THUMB2_INSN_MATCH(LDRW1, insn) || - THUMB2_INSN_MATCH(LDRBW1, insn) || - THUMB2_INSN_MATCH(LDRHW1, insn) || - THUMB2_INSN_MATCH(LDRD, insn) || - THUMB2_INSN_MATCH(LDRD1, insn) || - THUMB2_INSN_MATCH(LDREX, insn)) { - /* LDRx.W Rt, [PC, #+] -> - * LDRx.W Rt, [SP, #+] - (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(MUL, insn)) { - /* MUL Rd, Rn, SP */ - insns[2] = (insn & 0xfff0ffff) | 0x000d0000; - } else if (THUMB2_INSN_MATCH(DP, insn)) { - if (THUMB2_INSN_REG_RM(insn) == 15) - /* DP Rd, Rn, PC */ - insns[2] = (insn & 0xfff0ffff) | 0x000d0000; - else if (THUMB2_INSN_REG_RN(insn) == 15) - /* DP Rd, PC, Rm */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(LDRWL, insn)) { - /* LDRx.W Rt, [PC, #] -> - * LDRx.W Rt, [SP, #+] - * (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(RSBW, insn)) { - /* RSB{S}.W Rd, PC, # -> RSB{S}.W Rd, SP, # */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(RORW, insn) || - THUMB2_INSN_MATCH(LSLW1, insn) || - THUMB2_INSN_MATCH(LSRW1, insn)) { - if ((THUMB2_INSN_REG_RM(insn) == 15) && - (THUMB2_INSN_REG_RN(insn) == 15)) - /* ROR.W Rd, PC, PC */ - insns[2] = (insn & 0xfffdfffd); - else if (THUMB2_INSN_REG_RM(insn) == 15) - /* ROR.W Rd, Rn, PC */ - insns[2] = (insn & 0xfff0ffff) | 0xd0000; - else if (THUMB2_INSN_REG_RN(insn) == 15) - /* ROR.W Rd, PC, Rm */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(ROR, insn) || - THUMB2_INSN_MATCH(LSLW2, insn) || - THUMB2_INSN_MATCH(LSRW2, insn)) { - /* ROR{S} Rd, PC, # -> ROR{S} Rd, SP, # */ - insns[2] = (insn & 0xfff0ffff) | 0xd0000; - } - - if (THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn)) { - /* STRx.W Rt, [Rn, SP] */ - insns[2] = (insn & 0xfff0ffff) | 0x000d0000; - } else if (THUMB2_INSN_MATCH(STRD, insn) || - THUMB2_INSN_MATCH(STRHT, insn) || - THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHW1, insn)) { - if (THUMB2_INSN_REG_RN(insn) == 15) - /* STRD/T/HT{.W} Rt, [SP, ...] */ - insns[2] = (insn & 0xfffffff0) | 0xd; - else - insns[2] = insn; - } else if (THUMB2_INSN_MATCH(STRHW, insn) && - (THUMB2_INSN_REG_RN(insn) == 15)) { - if (THUMB2_INSN_REG_RN(insn) == 15) - /* STRH.W Rt, [SP, #-] */ - insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; - else - insns[2] = insn; - } - - /* STRx PC, xxx */ - if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn) || - THUMB2_INSN_MATCH(STRD, insn) || - THUMB2_INSN_MATCH(STRHT, insn) || - THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHW1, insn) || - THUMB2_INSN_MATCH(STRHW, insn))) { - insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000; - } - - if (THUMB2_INSN_MATCH(TEQ1, insn) || - THUMB2_INSN_MATCH(TST1, insn)) { - /* TEQ SP, # */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(TEQ2, insn) || - THUMB2_INSN_MATCH(TST2, insn)) { - if ((THUMB2_INSN_REG_RN(insn) == 15) && - (THUMB2_INSN_REG_RM(insn) == 15)) - /* TEQ/TST PC, PC */ - insns[2] = (insn & 0xfffdfffd); - else if (THUMB2_INSN_REG_RM(insn) == 15) - /* TEQ/TST Rn, PC */ - insns[2] = (insn & 0xfff0ffff) | 0xd0000; - else if (THUMB2_INSN_REG_RN(insn) == 15) - /* TEQ/TST PC, Rm */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } - - return 0; -} - -static int arch_make_trampoline_thumb(unsigned long vaddr, unsigned long insn, - unsigned long *tramp, size_t tramp_len) -{ - int ret; - int uregs = 0; - int pc_dep = 0; - unsigned int addr; - - ret = arch_check_insn_thumb(insn); - if (ret) { - pr_err("THUMB inst isn't support vaddr=%lx insn=%08lx\n", - vaddr, insn); - return ret; - } - - if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) { - uregs = 0x0700; /* 8-10 */ - pc_dep = 1; - } else if (THUMB_INSN_MATCH(MOV3, insn) && - (((((unsigned char)insn) & 0xff) >> 3) == 15)) { - /* MOV Rd, PC */ - uregs = 0x07; - pc_dep = 1; - } else if THUMB2_INSN_MATCH(ADR, insn) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if (((THUMB2_INSN_MATCH(LDRW, insn) || - THUMB2_INSN_MATCH(LDRW1, insn) || - THUMB2_INSN_MATCH(LDRBW, insn) || - THUMB2_INSN_MATCH(LDRBW1, insn) || - THUMB2_INSN_MATCH(LDRHW, insn) || - THUMB2_INSN_MATCH(LDRHW1, insn) || - THUMB2_INSN_MATCH(LDRWL, insn)) && - THUMB2_INSN_REG_RN(insn) == 15) || - THUMB2_INSN_MATCH(LDREX, insn) || - ((THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn) || - THUMB2_INSN_MATCH(STRHW, insn) || - THUMB2_INSN_MATCH(STRHW1, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RT(insn) == 15)) || - ((THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHT, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RT(insn) == 15))) { - uregs = 0xf000; /* Rt 12-15 */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(LDRD, insn) || - THUMB2_INSN_MATCH(LDRD1, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15)) { - uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(MUL, insn) && - THUMB2_INSN_REG_RM(insn) == 15) { - uregs = 0xf; - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(DP, insn) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0xf000; /* Rd 12-15 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(STRD, insn) && - ((THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_REG_RT(insn) == 15) || - THUMB2_INSN_REG_RT2(insn) == 15)) { - uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(RSBW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(RORW, insn) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0x0f00; - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(ROR, insn) || - THUMB2_INSN_MATCH(LSLW2, insn) || - THUMB2_INSN_MATCH(LSRW2, insn)) && - THUMB2_INSN_REG_RM(insn) == 15) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(LSLW1, insn) || - THUMB2_INSN_MATCH(LSRW1, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(TEQ1, insn) || - THUMB2_INSN_MATCH(TST1, insn)) && - THUMB2_INSN_REG_RN(insn) == 15) { - uregs = 0xf0000; /* Rn 0-3 (16-19) */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(TEQ2, insn) || - THUMB2_INSN_MATCH(TST2, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0xf0000; /* Rn 0-3 (16-19) */ - pc_dep = 1; - } - - if (unlikely(uregs && pc_dep)) { - memcpy(tramp, pc_dep_insn_execbuf_thumb, tramp_len); - prep_pc_dep_insn_execbuf_thumb(tramp, insn, uregs); - - addr = vaddr + 4; - *((unsigned short *)tramp + 13) = 0xdeff; - *((unsigned short *)tramp + 14) = addr & 0x0000ffff; - *((unsigned short *)tramp + 15) = addr >> 16; - if (!is_thumb2(insn)) { - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } else { - addr = vaddr + 4; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } - } else { - memcpy(tramp, gen_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - if (!is_thumb2(insn)) { - addr = vaddr + 2; - *((unsigned short *)tramp + 2) = insn; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } else { - addr = vaddr + 4; - tramp[1] = insn; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } - } - - if (THUMB_INSN_MATCH(B2, insn)) { - memcpy(tramp, b_off_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - addr = branch_t16_dest(insn, vaddr); - *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 15) = addr >> 16; - *((unsigned short *)tramp + 16) = 0; - *((unsigned short *)tramp + 17) = 0; - - } else if (THUMB_INSN_MATCH(B1, insn)) { - memcpy(tramp, b_cond_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - *((unsigned short *)tramp + 0) |= (insn & 0xf00); - addr = branch_cond_t16_dest(insn, vaddr); - *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 15) = addr >> 16; - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - - } else if (THUMB_INSN_MATCH(BLX2, insn) || - THUMB_INSN_MATCH(BX, insn)) { - memcpy(tramp, b_r_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - *((unsigned short *)tramp + 4) = insn; - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - - } else if (THUMB_INSN_MATCH(CBZ, insn)) { - memcpy(tramp, cbz_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - /* zero out original branch displacement (imm5 = 0; i = 0) */ - *((unsigned short *)tramp + 0) = insn & (~0x2f8); - /* replace it with 8 bytes offset in execbuf (imm5 = 0b00010) */ - *((unsigned short *)tramp + 0) |= 0x20; - addr = cbz_t16_dest(insn, vaddr); - *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 15) = addr >> 16; - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } - - return 0; -} - /** * @brief Prepares uprobe for ARM. * @@ -593,26 +80,10 @@ int arch_prepare_uprobe(struct uprobe *p) return -EINVAL; } - if (thumb_mode) { - ret = arch_make_trampoline_thumb(vaddr, insn, - tramp, tramp_len); - if (ret) { - struct decode_info info = { - .vaddr = vaddr, - .tramp = tramp, - .handeler = NULL, - }; - - ret = decode_thumb(insn, &info); - if (info.handeler) { - unsigned short *tr = (unsigned short *)tramp; - tr[13] = 0xdeff; /* breakpoint for uretprobe */ - p->ainsn.handler = info.handeler; - } - } - } else { - ret = arch_make_trampoline_arm(vaddr, insn, tramp); - } + if (thumb_mode) + ret = make_trampoline_thumb(p, vaddr, insn, tramp, tramp_len); + else + ret = make_trampoline_arm(vaddr, insn, tramp); if (ret) { pr_err("failed to make tramp, addr=%p\n", p->addr); @@ -652,13 +123,8 @@ int arch_prepare_uprobe(struct uprobe *p) void arch_opcode_analysis_uretprobe(struct uretprobe *rp) { /* Remove retprobe if first insn overwrites lr */ - rp->thumb_noret = !!(THUMB2_INSN_MATCH(BL, rp->up.opcode) || - THUMB2_INSN_MATCH(BLX1, rp->up.opcode) || - THUMB_INSN_MATCH(BLX2, rp->up.opcode)); - - rp->arm_noret = !!(ARM_INSN_MATCH(BL, rp->up.opcode) || - ARM_INSN_MATCH(BLX1, rp->up.opcode) || - ARM_INSN_MATCH(BLX2, rp->up.opcode)); + rp->thumb_noret = noret_thumb(rp->up.opcode); + rp->arm_noret = noret_arm(rp->up.opcode); } /** diff --git a/uprobe/arch/arm/swap-asm/thumb_tramps.c b/uprobe/arch/arm/swap-asm/thumb_tramps.c deleted file mode 100644 index 07bf494..0000000 --- a/uprobe/arch/arm/swap-asm/thumb_tramps.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) Samsung Electronics, 2015 - * - * 2015 Vyacheslav Cherkashin - * - */ - - -#include -#include -#include "trampoline_thumb.h" - - -#define URET_BP 0xdeff /* breakpoint for uretprobe */ - - -static void make_def(void *tramp, unsigned long insn, - unsigned long vaddr, bool t2) -{ - unsigned long ret_addr; - unsigned short *tr = tramp; - - /* - * thumb - +2 - * thumb2 - +4 - */ - ret_addr = vaddr + (2 << t2); - tr[4] = insn & 0x0000ffff; - if (t2) - tr[5] = insn >> 16; - - tr[13] = URET_BP; - tr[16] = (ret_addr & 0x0000ffff) | 0x1; - tr[17] = ret_addr >> 16; -} - -void tt_make_common(void *tramp, unsigned long insn, - unsigned long vaddr, bool t2) -{ memcpy(tramp, gen_insn_execbuf_thumb, 4 * UPROBES_TRAMP_LEN); - make_def(tramp, insn, vaddr, t2); -} - -void tt_make_pc_deps(void *tramp, unsigned long mod_insn, - unsigned long vaddr, bool t2) -{ - unsigned long pc_val = vaddr + 4; - unsigned short *tr = tramp; - - memcpy(tramp, pc_dep_insn_execbuf_thumb, 4 * UPROBES_TRAMP_LEN); - make_def(tramp, mod_insn, vaddr, t2); - - /* save PC value */ - tr[14] = pc_val & 0x0000ffff; - tr[15] = pc_val >> 16; -} diff --git a/uprobe/arch/arm/swap-asm/trampoline_thumb.S b/uprobe/arch/arm/swap-asm/trampoline_thumb.S deleted file mode 100644 index 5fee5f9..0000000 --- a/uprobe/arch/arm/swap-asm/trampoline_thumb.S +++ /dev/null @@ -1,134 +0,0 @@ - .thumb - - .global gen_insn_execbuf_thumb -gen_insn_execbuf_thumb: - nop - nop - nop // original instruction - nop // original instruction - nop - nop - nop - sub sp, sp, #8 - str r0, [sp, #0] - ldr r0, [pc, #12] - str r0, [sp, #4] - nop - pop {r0, pc} // ssbreak - nop // retbreak - nop - nop - nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - nop - - .global pc_dep_insn_execbuf_thumb - .align 4 -pc_dep_insn_execbuf_thumb: - push {r6, r7} - ldr r6, i1 - mov r7, sp - mov sp, r6 - nop // PC -> SP - nop // PC -> SP - mov sp, r7 - pop {r6, r7} - push {r0, r1} - ldr r0, i2 - nop - str r0, [sp, #4] - pop {r0, pc} // ssbreak - nop // retbreak -i1: nop // stored PC hi - nop // stored PC lo -i2: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global b_r_insn_execbuf_thumb - .align 4 -b_r_insn_execbuf_thumb: - nop - nop - nop - nop - nop // bx,blx (Rm) - nop // - push {r0,r1} - ldr r0, np - nop - str r0, [sp, #4] - pop {r0,pc} - nop - nop // ssbreak - nop // retbreak - nop - nop -np: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global b_off_insn_execbuf_thumb - .align 4 -b_off_insn_execbuf_thumb: - push {r0,r1} - ldr r0, bd - str r0, [sp, #4] - pop {r0, pc} - nop - nop - push {r0,r1} - ldr r0, np2 - nop - str r0, [sp, #4] - pop {r0,pc} - nop - nop // ssbreak - nop // retbreak -bd: nop // branch displacement hi - nop // branch displacement lo -np2: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global b_cond_insn_execbuf_thumb - .align 4 -b_cond_insn_execbuf_thumb: - beq condway - push {r0,r1} - ldr r0, np4 - nop - str r0, [sp, #4] - pop {r0,pc} -condway: push {r0,r1} - ldr r0, bd4 - str r0, [sp, #4] - pop {r0,pc} - nop - nop - nop // ssbreak - nop // retbreak -bd4: nop // branch displacement hi - nop // branch displacement lo -np4: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global cbz_insn_execbuf_thumb - .align 4 -cbz_insn_execbuf_thumb: - nop // cbz - push {r0,r1} - ldr r0, np5 - nop - str r0, [sp, #4] - pop {r0,pc} - push {r0,r1} - ldr r0, bd5 - str r0, [sp, #4] - pop {r0,pc} - nop - nop - nop // ssbreak - nop // retbreak -bd5: nop // branch displacement hi - nop // branch displacement lo -np5: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo -- 2.7.4