mirror of
https://github.com/torvalds/linux.git
synced 2024-12-03 17:41:22 +00:00
ARM: kprobes: collects stack consumption for store instructions
This patch uses the previously introduced checker functionality on store instructions to record their stack consumption information to arch_probes_insn. Signed-off-by: Wang Nan <wangnan0@huawei.com> Reviewed-by: Jon Medhurst <tixy@linaro.org> Signed-off-by: Jon Medhurst <tixy@linaro.org>
This commit is contained in:
parent
83803d97da
commit
6624cf651f
@ -38,6 +38,7 @@ struct arch_probes_insn {
|
||||
probes_check_cc *insn_check_cc;
|
||||
probes_insn_singlestep_t *insn_singlestep;
|
||||
probes_insn_fn_t *insn_fn;
|
||||
int stack_space;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -425,6 +425,16 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
*/
|
||||
probes_opcode_t origin_insn = insn;
|
||||
|
||||
/*
|
||||
* stack_space is initialized to 0 here. Checker functions
|
||||
* should update is value if they find this is a stack store
|
||||
* instruction: positive value means bytes of stack usage,
|
||||
* negitive value means unable to determine stack usage
|
||||
* statically. For instruction doesn't store to stack, checker
|
||||
* do nothing with it.
|
||||
*/
|
||||
asi->stack_space = 0;
|
||||
|
||||
if (emulate)
|
||||
insn = prepare_emulated_insn(insn, asi, thumb);
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
obj-$(CONFIG_KPROBES) += core.o actions-common.o
|
||||
obj-$(CONFIG_KPROBES) += core.o actions-common.o checkers-common.o
|
||||
obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
|
||||
test-kprobes-objs := test-core.o
|
||||
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
obj-$(CONFIG_KPROBES) += actions-thumb.o
|
||||
obj-$(CONFIG_KPROBES) += actions-thumb.o checkers-thumb.o
|
||||
test-kprobes-objs += test-thumb.o
|
||||
else
|
||||
obj-$(CONFIG_KPROBES) += actions-arm.o
|
||||
obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o
|
||||
test-kprobes-objs += test-arm.o
|
||||
endif
|
||||
|
@ -64,6 +64,7 @@
|
||||
|
||||
#include "../decode-arm.h"
|
||||
#include "core.h"
|
||||
#include "checkers.h"
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 6
|
||||
#define BLX(reg) "blx "reg" \n\t"
|
||||
@ -340,4 +341,4 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
|
||||
};
|
||||
|
||||
const struct decode_checker *kprobes_arm_checkers[] = {NULL};
|
||||
const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, NULL};
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "../decode-thumb.h"
|
||||
#include "core.h"
|
||||
#include "checkers.h"
|
||||
|
||||
/* These emulation encodings are functionally equivalent... */
|
||||
#define t32_emulate_rd8rn16rm0ra12_noflags \
|
||||
@ -665,5 +666,5 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = {
|
||||
.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
|
||||
};
|
||||
|
||||
const struct decode_checker *kprobes_t32_checkers[] = {NULL};
|
||||
const struct decode_checker *kprobes_t16_checkers[] = {NULL};
|
||||
const struct decode_checker *kprobes_t32_checkers[] = {t32_stack_checker, NULL};
|
||||
const struct decode_checker *kprobes_t16_checkers[] = {t16_stack_checker, NULL};
|
||||
|
99
arch/arm/probes/kprobes/checkers-arm.c
Normal file
99
arch/arm/probes/kprobes/checkers-arm.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers-arm.c
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "../decode.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "checkers.h"
|
||||
|
||||
static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
/*
|
||||
* PROBES_LDRSTRD, PROBES_LDMSTM, PROBES_STORE,
|
||||
* PROBES_STORE_EXTRA may get here. Simply mark all normal
|
||||
* insns as STACK_USE_NONE.
|
||||
*/
|
||||
static const union decode_item table[] = {
|
||||
/*
|
||||
* 'STR{,D,B,H}, Rt, [Rn, Rm]' should be marked as UNKNOWN
|
||||
* if Rn or Rm is SP.
|
||||
* x
|
||||
* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx
|
||||
* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_OR (0x0e10000f, 0x0600000d),
|
||||
DECODE_OR (0x0e1f0000, 0x060d0000),
|
||||
|
||||
/*
|
||||
* x
|
||||
* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
|
||||
* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx
|
||||
*/
|
||||
DECODE_OR (0x0e5000bf, 0x000000bd),
|
||||
DECODE_CUSTOM (0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN),
|
||||
|
||||
/*
|
||||
* For PROBES_LDMSTM, only stmdx sp, [...] need to examine
|
||||
*
|
||||
* Bit B/A (bit 24) encodes arithmetic operation order. 1 means
|
||||
* before, 0 means after.
|
||||
* Bit I/D (bit 23) encodes arithmetic operation. 1 means
|
||||
* increment, 0 means decrement.
|
||||
*
|
||||
* So:
|
||||
* B I
|
||||
* / /
|
||||
* A D | Rn |
|
||||
* STMDX SP, [...] cccc 100x 00x0 xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_CUSTOM (0x0edf0000, 0x080d0000, STACK_USE_STMDX),
|
||||
|
||||
/* P U W | Rn | Rt | imm12 |*/
|
||||
/* STR (immediate) cccc 010x x0x0 1101 xxxx xxxx xxxx xxxx */
|
||||
/* STRB (immediate) cccc 010x x1x0 1101 xxxx xxxx xxxx xxxx */
|
||||
/* P U W | Rn | Rt |imm4| |imm4|*/
|
||||
/* STRD (immediate) cccc 000x x1x0 1101 xxxx xxxx 1111 xxxx */
|
||||
/* STRH (immediate) cccc 000x x1x0 1101 xxxx xxxx 1011 xxxx */
|
||||
/*
|
||||
* index = (P == '1'); add = (U == '1').
|
||||
* Above insns with:
|
||||
* index == 0 (str{,d,h} rx, [sp], #+/-imm) or
|
||||
* add == 1 (str{,d,h} rx, [sp, #+<imm>])
|
||||
* should be STACK_USE_NONE.
|
||||
* Only str{,b,d,h} rx,[sp,#-n] (P == 1 and U == 0) are
|
||||
* required to be examined.
|
||||
*/
|
||||
/* STR{,B} Rt,[SP,#-n] cccc 0101 0xx0 1101 xxxx xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0x0f9f0000, 0x050d0000, STACK_USE_FIXED_XXX),
|
||||
|
||||
/* STR{D,H} Rt,[SP,#-n] cccc 0001 01x0 1101 xxxx xxxx 1x11 xxxx */
|
||||
DECODE_CUSTOM (0x0fdf00b0, 0x014d00b0, STACK_USE_FIXED_X0X),
|
||||
|
||||
/* fall through */
|
||||
DECODE_CUSTOM (0, 0, STACK_USE_NONE),
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
|
||||
}
|
||||
|
||||
const struct decode_checker arm_stack_checker[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_LDRSTRD] = {.checker = arm_check_stack},
|
||||
[PROBES_STORE_EXTRA] = {.checker = arm_check_stack},
|
||||
[PROBES_STORE] = {.checker = arm_check_stack},
|
||||
[PROBES_LDMSTM] = {.checker = arm_check_stack},
|
||||
};
|
101
arch/arm/probes/kprobes/checkers-common.c
Normal file
101
arch/arm/probes/kprobes/checkers-common.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers-common.c
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "../decode.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "checkers.h"
|
||||
|
||||
enum probes_insn checker_stack_use_none(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
asi->stack_space = 0;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
enum probes_insn checker_stack_use_unknown(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
asi->stack_space = -1;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
enum probes_insn checker_stack_use_imm_0xx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = insn & 0xff;
|
||||
asi->stack_space = imm;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Different from other insn uses imm8, the real addressing offset of
|
||||
* STRD in T32 encoding should be imm8 * 4. See ARMARM description.
|
||||
*/
|
||||
enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = insn & 0xff;
|
||||
asi->stack_space = imm << 2;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
#else
|
||||
enum probes_insn checker_stack_use_imm_x0x(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = ((insn & 0xf00) >> 4) + (insn & 0xf);
|
||||
asi->stack_space = imm;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum probes_insn checker_stack_use_imm_xxx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = insn & 0xfff;
|
||||
asi->stack_space = imm;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
enum probes_insn checker_stack_use_stmdx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
unsigned int reglist = insn & 0xffff;
|
||||
int pbit = insn & (1 << 24);
|
||||
asi->stack_space = (hweight32(reglist) - (!pbit ? 1 : 0)) * 4;
|
||||
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
const union decode_action stack_check_actions[] = {
|
||||
[STACK_USE_NONE] = {.decoder = checker_stack_use_none},
|
||||
[STACK_USE_UNKNOWN] = {.decoder = checker_stack_use_unknown},
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
[STACK_USE_FIXED_0XX] = {.decoder = checker_stack_use_imm_0xx},
|
||||
[STACK_USE_T32STRD] = {.decoder = checker_stack_use_t32strd},
|
||||
#else
|
||||
[STACK_USE_FIXED_X0X] = {.decoder = checker_stack_use_imm_x0x},
|
||||
#endif
|
||||
[STACK_USE_FIXED_XXX] = {.decoder = checker_stack_use_imm_xxx},
|
||||
[STACK_USE_STMDX] = {.decoder = checker_stack_use_stmdx},
|
||||
};
|
110
arch/arm/probes/kprobes/checkers-thumb.c
Normal file
110
arch/arm/probes/kprobes/checkers-thumb.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers-thumb.c
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "../decode.h"
|
||||
#include "../decode-thumb.h"
|
||||
#include "checkers.h"
|
||||
|
||||
static enum probes_insn __kprobes t32_check_stack(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
/*
|
||||
* PROBES_T32_LDMSTM, PROBES_T32_LDRDSTRD and PROBES_T32_LDRSTR
|
||||
* may get here. Simply mark all normal insns as STACK_USE_NONE.
|
||||
*/
|
||||
static const union decode_item table[] = {
|
||||
|
||||
/*
|
||||
* First, filter out all ldr insns to make our life easier.
|
||||
* Following load insns may come here:
|
||||
* LDM, LDRD, LDR.
|
||||
* In T32 encoding, bit 20 is enough for distinguishing
|
||||
* load and store. All load insns have this bit set, when
|
||||
* all store insns have this bit clear.
|
||||
*/
|
||||
DECODE_CUSTOM (0x00100000, 0x00100000, STACK_USE_NONE),
|
||||
|
||||
/*
|
||||
* Mark all 'STR{,B,H}, Rt, [Rn, Rm]' as STACK_USE_UNKNOWN
|
||||
* if Rn or Rm is SP. T32 doesn't encode STRD.
|
||||
*/
|
||||
/* xx | Rn | Rt | | Rm |*/
|
||||
/* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
|
||||
/* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
|
||||
/* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
|
||||
/* INVALID INSN 1111 1000 0110 xxxx xxxx 0000 00xx xxxx */
|
||||
/* By Introducing INVALID INSN, bit 21 and 22 can be ignored. */
|
||||
DECODE_OR (0xff9f0fc0, 0xf80d0000),
|
||||
DECODE_CUSTOM (0xff900fcf, 0xf800000d, STACK_USE_UNKNOWN),
|
||||
|
||||
|
||||
/* xx | Rn | Rt | PUW| imm8 |*/
|
||||
/* STR (imm 8) 1111 1000 0100 1101 xxxx 110x xxxx xxxx */
|
||||
/* STRB (imm 8) 1111 1000 0000 1101 xxxx 110x xxxx xxxx */
|
||||
/* STRH (imm 8) 1111 1000 0010 1101 xxxx 110x xxxx xxxx */
|
||||
/* INVALID INSN 1111 1000 0110 1101 xxxx 110x xxxx xxxx */
|
||||
/* Only consider U == 0 and P == 1: strx rx, [sp, #-<imm>] */
|
||||
DECODE_CUSTOM (0xff9f0e00, 0xf80d0c00, STACK_USE_FIXED_0XX),
|
||||
|
||||
/* For STR{,B,H} (imm 12), offset is always positive, so ignore them. */
|
||||
|
||||
/* P U W | Rn | Rt | Rt2| imm8 |*/
|
||||
/* STRD (immediate) 1110 1001 01x0 1101 xxxx xxxx xxxx xxxx */
|
||||
/*
|
||||
* Only consider U == 0 and P == 1.
|
||||
* Also note that STRD in T32 encoding is special:
|
||||
* imm = ZeroExtend(imm8:'00', 32)
|
||||
*/
|
||||
DECODE_CUSTOM (0xffdf0000, 0xe94d0000, STACK_USE_T32STRD),
|
||||
|
||||
/* | Rn | */
|
||||
/* STMDB 1110 1001 00x0 1101 xxxx xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0xffdf0000, 0xe90d0000, STACK_USE_STMDX),
|
||||
|
||||
/* fall through */
|
||||
DECODE_CUSTOM (0, 0, STACK_USE_NONE),
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
|
||||
}
|
||||
|
||||
const struct decode_checker t32_stack_checker[NUM_PROBES_T32_ACTIONS] = {
|
||||
[PROBES_T32_LDMSTM] = {.checker = t32_check_stack},
|
||||
[PROBES_T32_LDRDSTRD] = {.checker = t32_check_stack},
|
||||
[PROBES_T32_LDRSTR] = {.checker = t32_check_stack},
|
||||
};
|
||||
|
||||
/*
|
||||
* See following comments. This insn must be 'push'.
|
||||
*/
|
||||
static enum probes_insn __kprobes t16_check_stack(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
unsigned int reglist = insn & 0x1ff;
|
||||
asi->stack_space = hweight32(reglist) * 4;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* T16 encoding is simple: only the 'push' insn can need extra stack space.
|
||||
* Other insns, like str, can only use r0-r7 as Rn.
|
||||
*/
|
||||
const struct decode_checker t16_stack_checker[NUM_PROBES_T16_ACTIONS] = {
|
||||
[PROBES_T16_PUSH] = {.checker = t16_check_stack},
|
||||
};
|
54
arch/arm/probes/kprobes/checkers.h
Normal file
54
arch/arm/probes/kprobes/checkers.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers.h
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _ARM_KERNEL_PROBES_CHECKERS_H
|
||||
#define _ARM_KERNEL_PROBES_CHECKERS_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include "../decode.h"
|
||||
|
||||
extern probes_check_t checker_stack_use_none;
|
||||
extern probes_check_t checker_stack_use_unknown;
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
extern probes_check_t checker_stack_use_imm_0xx;
|
||||
#else
|
||||
extern probes_check_t checker_stack_use_imm_x0x;
|
||||
#endif
|
||||
extern probes_check_t checker_stack_use_imm_xxx;
|
||||
extern probes_check_t checker_stack_use_stmdx;
|
||||
|
||||
enum {
|
||||
STACK_USE_NONE,
|
||||
STACK_USE_UNKNOWN,
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
STACK_USE_FIXED_0XX,
|
||||
STACK_USE_T32STRD,
|
||||
#else
|
||||
STACK_USE_FIXED_X0X,
|
||||
#endif
|
||||
STACK_USE_FIXED_XXX,
|
||||
STACK_USE_STMDX,
|
||||
NUM_STACK_USE_TYPES
|
||||
};
|
||||
|
||||
extern const union decode_action stack_check_actions[];
|
||||
|
||||
#ifndef CONFIG_THUMB2_KERNEL
|
||||
extern const struct decode_checker arm_stack_checker[];
|
||||
#else
|
||||
#endif
|
||||
extern const struct decode_checker t32_stack_checker[];
|
||||
extern const struct decode_checker t16_stack_checker[];
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user