How to set a hardware breakpoint in your program (set data breakpoints with program code instead of JTAG)

Source: Internet
Author: User
Tags goto volatile

The recent Android project encountered a memory-crushing problem, by analyzing the log to find the memory is trampled on the address, but can not find who stepped down. Generally the problem of stepping on memory, you can use the hardware data breakpoints to find the perpetrators. But in this project, stepping on the memory is in the Android boot process occurs, too late on Jtag, the other is the RAM is dynamically allocated, each boot is different (but always stepping on the fixed byte of the assigned address), can not specify the breakpoint address (if the global variable is trampled, its address is generally unchanged , can be directly on the address of the data breakpoint to be trampled, so in this case only through the program to give themselves the next breakpoint (self-host), the program allocates memory, immediately to the memory of the next data breakpoint (known to step memory on this address), when other programs accidentally write to the memory, An abnormal interruption will occur, thus finding the perpetrators of the ram-stepping.


Unfortunately, after Baidu, there is almost no article about how to do this thing. The GDB code was turned over and no hardware breakpoints were seen in arm (the GDB version used was too old). Later a multi-party search, find some information, recorded in the following:

1. In the ARM's architecture document (available for download on ARM's website), Cortext A8, A9, A15 support hardware breakpoints (CP14), DBGWCR and Jtag with coprocessor DBGWVR Operation Debug Register Watchpoint and processor Debuger can operate them) (now the machines that run Android are generally A9 above, so hardware breakpoints are basically supported).

2. The Linux kernel is not supported to add hardware breakpoints to arm after the 2.6.37 version.

3. GDB does not support adding hardware breakpoints to arm after the 7.3 version (the latest version is 7.8.1,2014 October).


If it is your own arm system, you can write the assembly, operate the CP14 through the MCR coprocessor instruction, and read and write DBGWCR and DBGWVR to the hardware breakpoints.

If the Linux system (2.6.37 version above), it is simple, you can use the Ptrace function to manipulate DBGWCR and DBGWVR, under hardware breakpoints.

The following is a partial code for a hardware breakpoint that is intercepted from GDB 7.8.1:

  for (i = 0; i < Arm_linux_get_hw_breakpoint_count (); i++) if (Arm_lwp_info->bpts_changed[i]) {
        errno = 0; if (arm_hwbp_control_is_enabled (Bpts[i].control)) if (<span style= "color: #ff0000;" ><strong>ptrace (Ptrace_sethbpregs, PID, (PTRACE_TYPE_ARG3) ((I << 1) + 1), &bpts[i].addr

        ESS) </strong></span> < 0) Perror_with_name (_ ("Unexpected error setting breakpoint")); if (Bpts[i].control! = 0) if (<span style= "color: #ff0000;" ><strong>ptrace (Ptrace_sethbpregs, PID, (PTRACE_TYPE_ARG3) ((I << 1) + 2), &bpts[i].cont

        ROL) </strong></span> < 0) Perror_with_name (_ ("Unexpected error setting breakpoint"));
      Arm_lwp_info->bpts_changed[i] = 0;
        } for (i = 0; i < Arm_linux_get_hw_watchpoint_count (); i++) if (Arm_lwp_info->wpts_changed[i]) {
        errno = 0; if (Arm_hwbp_cOntrol_is_enabled (Wpts[i].control)) if (<span style= "color: #ff0000;" ><strong>ptrace (Ptrace_sethbpregs, PID, (PTRACE_TYPE_ARG3)-((<< 1) + 1), &wpts[i].add

        ress) </strong></span> < 0) Perror_with_name (_ ("Unexpected error setting watchpoint")); if (Wpts[i].control! = 0) if (<span style= "color: #ff0000;" ><strong>ptrace (Ptrace_sethbpregs, PID, (PTRACE_TYPE_ARG3)-((<< 1) + 2), &wpts[i].con

        Trol) </strong></span> < 0) Perror_with_name (_ ("Unexpected error setting watchpoint"));
      Arm_lwp_info->wpts_changed[i] = 0; }

As you can see, calling Ptrace, parameters can read and write hardware breakpoint registers using Ptrace_gethbpregs and Ptrace_sethbpregs, where the 3rd parameter is the index number of the register, the positive is the read-write breakpoint register, Negative numbers are read-write Watchpoint registers (that is, data breakpoints). +1,+2 is the address and control register that reads and writes the 1th breakpoint, +3,+4 is the address and control register of the 2nd breakpoint, and so on, and -1,-2 is the address and control register that reads and writes the 1th data breakpoint, -3,-4 is the address and control register that reads and writes the 2nd data breakpoint, and so on. If the 3rd parameter is 0, it means reading debugging information, such as how many hardware breakpoints are supported, how many data breakpoints, and so on.


Look at the code for the Linux kernel 3.1.10 and see the implementation of Ptrace, which is the Ptrace code in 3.1.10/KERNEL/PTRACE.C:

Syscall_define4 (Ptrace, long, request, long, PID, unsigned long, addr, unsigned long, data)

It calls the schema-related Arch_ptrace function to implement the Debug function, which in fact is now 3.1.10/arch/arm/kernel/ptrace.c in arm:

Long Arch_ptrace (struct task_struct *child, long request, unsigned long addr, unsigned Long data)

In this function, the Ptrace_gethbpregs and Ptrace_sethbpregs functions are called for Ptrace_gethbpregs and Ptrace_sethbpregs respectively:

#ifdef config_have_hw_breakpoint case
		Ptrace_gethbpregs:
			if (ptrace_get_breakpoints (child) < 0)
				Return-esrch;

			ret = Ptrace_gethbpregs (Child, addr,
						(unsigned long __user *) data);
			Ptrace_put_breakpoints (child);
			break;
		Case Ptrace_sethbpregs: The
			if (ptrace_get_breakpoints (child) < 0)
				Return-esrch;

			ret = Ptrace_sethbpregs (Child, addr,
						(unsigned long __user *) data);
			Ptrace_put_breakpoints (child);
			break;
#endif


In Ptrace_gethbpregs, if NUM is 0, call Ptrace_get_hbp_resource_info to get debug information, and if NUM is not 0, first call ptrace_hbp_num_to_ IDX converts num to index (as per the previous +1,+2, +3,+4, ...). The rule transforms, and then returns the debug configuration information cached in the thread.

static int Ptrace_gethbpregs (struct task_struct *tsk, long num,
			     unsigned long  __user *data)
{
	u32 reg;< C4/>int idx, ret = 0;
	struct perf_event *bp;
	struct Arch_hw_breakpoint_ctrl Arch_ctrl;

	if (num = = 0) {
		reg = Ptrace_get_hbp_resource_info ();
	} else {
		idx = ptrace_hbp_num_to_idx (num);
		if (idx < 0 | | idx >= arm_max_hbp_slots) {
			ret =-einval;
			goto out;
		}

		bp = tsk->thread.debug.hbp[idx];
		if (!BP) {
			reg = 0;
			Goto put;
		}

		Arch_ctrl = COUNTER_ARCH_BP (BP)->ctrl;

		/
		 * Fix up the Len because we are adjusted it * to compensate for an
		 unaligned address.
		 *
		/while (!) ( Arch_ctrl.len & 0x1))
			arch_ctrl.len >>= 1;

		if (num & 0x1)
			reg = bp->attr.bp_addr;
		else
			reg = Encode_ctrl_reg (Arch_ctrl);
	}

Put:
	if (Put_user (reg, data))
		ret =-efault;

Out:
	return ret;
}


Follow Ptrace_get_hbp_resource_info all the way down, its call hw_breakpoint_slots, the latter call Get_num_brps, and then the latter call Arm_dbg_read read the value of the debug register.

In Ptrace_get_hbp_resurce_info, it can be seen that its return value is composed of 4 bytes, the highest byte is the system support hardware broken point (maximum not more than 16), the second high byte is the system support data break point (watchpoint) (maximum not more than 16), The second low byte is the maximum length supported by the Watchpoint, and the lowest byte is the debug information version number:

Static u32 ptrace_get_hbp_resource_info (void) {U8 Num_brps, Num_wrps, Debug_arch, Wp_len;

	U32 reg = 0;
	Num_brps = Hw_breakpoint_slots (type_inst);
	Num_wrps = Hw_breakpoint_slots (Type_data);
	Debug_arch = Arch_get_debug_arch ();

	Wp_len = Arch_get_max_wp_len ();
	Reg |= Debug_arch;
	Reg <<= 8;
	Reg |= Wp_len;
	Reg <<= 8;
	Reg |= Num_wrps;
	Reg <<= 8;

	Reg |= Num_brps;
return reg;

	} int hw_breakpoint_slots (int type) {if (!debug_arch_supported ()) return 0;
	 /* * We can be called early and so don ' t rely on * Our static variables being initialised.
	*/switch (type) {case Type_inst:return get_num_brps ();
	Case Type_data:return Get_num_wrps ();
		Default:pr_warning ("Unknown slot type:%d\n", type);
	return 0; }}/* Determine number of usable brps available.
	*/static int get_num_brps (void) {int brps = get_num_brp_resources ();
	if (Core_has_mismatch_brps ()) Brps-= Get_num_reserved_brps ();
return Brps; }/* Determine number of BRP reGister available.
	*/static int get_num_brp_resources (void) {u32 didr;
	Arm_dbg_read (C0, 0, DIDR);
Return ((Didr >>) & 0xf) + 1;
 }

The most crucial thing to see is the implementation of Arm_dbg_read. Arm_dbg_read and Arm_dbg_write are actually a set of macros that are used to read and write debug registers, which are a compilation on ARM, which is defined by the MRC and MCR operations coprocessor CP14 to perform the debugging functions as follows:

/* Accessor macros for the debug registers. *
/#define ARM_DBG_READ (M, OP2, VAL) do {\
	asm volatile ("MRC P14, 0,%0, C0," #M "," #OP2: "=r" (VAL)); \
} w Hile (0)

#define Arm_dbg_write (M, OP2, val) do {\
	asm volatile ("MCR p14, 0,%0, C0," #M "," #OP2:: "R" (val)); \
} while (0)

The above see how Linux operates CP14 to get debug information.


Continue to see the implementation of Ptrace_sethbpregs, which verifies the validity of the input information, and then uses DECODE_CTRL_REG to synthesize the information group to write to the DBG register value, and finally call Modify_user_hw_ Breakpoint to commit the set action.

static int Ptrace_sethbpregs (struct task_struct *tsk, long num, unsigned long __user *data) {int idx, Gen_len, G
	En_type, implied_type, ret = 0;
	U32 User_val;
	struct Perf_event *bp;
	struct Arch_hw_breakpoint_ctrl Ctrl;

	struct perf_event_attr attr;
	if (num = = 0) goto out;
	else if (num < 0) Implied_type = HW_BREAKPOINT_RW;

	else Implied_type = hw_breakpoint_x;
	IDX = PTRACE_HBP_NUM_TO_IDX (num);
		if (idx < 0 | | idx >= arm_max_hbp_slots) {ret =-einval;
	Goto out;
		} if (Get_user (user_val, data)) {ret =-efault;
	Goto out;
	} BP = tsk->thread.debug.hbp[idx];
		if (!bp) {bp = Ptrace_hbp_create (tsk, Implied_type);
			if (Is_err (BP)) {ret = Ptr_err (BP);
		Goto out;
	} Tsk->thread.debug.hbp[idx] = BP;

	} attr = bp->attr;
	if (num & 0x1) {/* Address */attr.bp_addr = User_val;
		} else {/* Control */Decode_ctrl_reg (user_val, &ctrl);
		RET = Arch_bp_generic_fields (CTRL, &gen_len, &gen_type);
			if (ret)Goto out;
			if (Gen_type & implied_type)! = Gen_type) {ret =-einval;
		Goto out;
		} Attr.bp_len = Gen_len;
		Attr.bp_type = Gen_type;
	attr.disabled =!ctrl.enabled;
} ret = Modify_user_hw_breakpoint (BP, &attr);
Out:return ret;
 }


Modify_user_hw_breakpoint just submits the action through perf_event_enable.

int modify_user_hw_breakpoint (struct perf_event *bp, struct perf_event_attr *attr)
{
	U64 old_addr = bp-> attr.bp_addr;
	U64 Old_len = bp->attr.bp_len;
	int old_type = bp->attr.bp_type;
	int err = 0;

	Perf_event_disable (BP);

	BP->ATTR.BP_ADDR = attr->bp_addr;
	Bp->attr.bp_type = attr->bp_type;
	Bp->attr.bp_len = attr->bp_len;

	if (attr->disabled)
		goto end;

	Err = Validate_hw_breakpoint (BP);
	if (!err)
		perf_event_enable (BP);

	if (err) {
		bp->attr.bp_addr = old_addr;
		Bp->attr.bp_type = Old_type;
		Bp->attr.bp_len = Old_len;
		if (!bp->attr.disabled)
			perf_event_enable (BP);

		return err;
	}

End:
	bp->attr.disabled = attr->disabled;

	return 0;
}

In order to see how the breakpoint above perf_event works, it needs to be seen from the Init_hw_breakpoint function in 3.1.10/kernel/events/hw_breakpoint.c:

int __init init_hw_breakpoint (void)
{
	unsigned int **task_bp_pinned;
	int CPU, ERR_CPU;
	int i;

	for (i = 0; i < Type_max; i++)
		nr_slots[i] = hw_breakpoint_slots (i);

	FOR_EACH_POSSIBLE_CPU (CPU) {

	}

	constraints_initialized = 1;

	Perf_pmu_register (&perf_breakpoint, "breakpoint", perf_type_breakpoint);

	Return Register_die_notifier (&HW_BREAKPOINT_EXCEPTIONS_NB);

 Err_alloc:
}


This registers the operation of the perf_breakpoint and the definition of the global variable Perf_breakpoint:

static struct PMU Perf_breakpoint = {
	. task_ctx_nr	= perf_sw_context,/* could eventually get its own */

	. E Vent_init	= Hw_breakpoint_event_init,
	. Add		= Hw_breakpoint_add,
	. del		= Hw_breakpoint_del ,
	. Start		= Hw_breakpoint_start,
	. Stop		= Hw_breakpoint_stop,
	. Read		= Hw_breakpoint_ Pmu_read,
};

So the addition of hardware breakpoints will go into the Hw_breakpoint_add function, and the Hw_breakpoint_add function simply calls the schema-related function Arch_install_hw_breakpoint to implement, in arm, The function is in 3.1.10/arch/arm/kernel/hw_breakpoint.c:

static int Hw_breakpoint_add (struct perf_event *bp, int flags) {if (!) (

	Flags & Perf_ef_start)) bp->hw.state = perf_hes_stopped;
Return Arch_install_hw_breakpoint (BP);
 }/* * Install a perf counter breakpoint.
	*/int Arch_install_hw_breakpoint (struct perf_event *bp) {struct Arch_hw_breakpoint *info = COUNTER_ARCH_BP (BP);
	struct perf_event **slot, **slots;
	int I, Max_slots, Ctrl_base, val_base, ret = 0;

	U32 addr, Ctrl; /* Ensure that we were in monitor mode and halting mode is disabled.
	*/ret = Enable_monitor_mode ();

	if (ret) goto out;
	addr = info->address; CTRL = Encode_ctrl_reg (Info->ctrl) |

	0x1
		if (Info->ctrl.type = = Arm_breakpoint_execute) {/* Breakpoint */ctrl_base = ARM_BASE_BCR;
		Val_base = Arm_base_bvr;
		Slots = (struct perf_event * *) __get_cpu_var (BP_ON_REG);
		Max_slots = Core_num_brps; if (info->step_ctrl.enabled) {/* Override the breakpoint data with the step data. */addr = Info->trigger &am P
			~0x3; CTRL = ENCOde_ctrl_reg (Info->step_ctrl);
			}} else {/* Watchpoint */if (info->step_ctrl.enabled) {/* Install into the reserved breakpoint region. */
			Ctrl_base = arm_base_bcr + Core_num_brps;
			Val_base = Arm_base_bvr + Core_num_brps; /* Override The Watchpoint data with the step data.
			*/addr = Info->trigger & ~0x3;
		CTRL = Encode_ctrl_reg (Info->step_ctrl);
			} else {ctrl_base = ARM_BASE_WCR;
		Val_base = ARM_BASE_WVR;
		} slots = (struct perf_event * *) __get_cpu_var (WP_ON_REG);
	Max_slots = Core_num_wrps;

		} for (i = 0; i < max_slots; ++i) {slot = &slots[i];
			if (!*slot) {*slot = BP;
		Break
		}} if (warn_once (i = = max_slots, "Can ' t find any breakpoint slot\n")) {ret =-ebusy;
	Goto out; }/* Setup the address register.

	*/Write_wb_reg (val_base + i, addr); /* Setup the control register.

*/Write_wb_reg (ctrl_base + i, ctrl);
Out:return ret;
 }
Arch_install_hw_breakpoint The information combination (ENCODE_CTRL_REG), and then calls Write_wb_reg to write the breakpoint address information and breakpoint control information to the Debug register, respectively.


Write_wb_reg is a series of macros that are composed of CP14 operations, and the macros are referred to as follows, eventually calling the Arm_dbg_write macro to write the value, which is written to the debug register through the assembler operation Coprocessor CP14.

static void Write_wb_reg (int n, u32 val) {switch (n) {gen_write_wb_reg_cases (Arm_op2_bvr, Val);
	Gen_write_wb_reg_cases (Arm_op2_bcr, Val);
	Gen_write_wb_reg_cases (ARM_OP2_WVR, Val);
	Gen_write_wb_reg_cases (ARM_OP2_WCR, Val);
	Default:pr_warning ("Attempt to write to unknown breakpoint" "Register%d\n", n);
} ISB ();		} #define Gen_write_wb_reg_cases (OP2, Val) \ write_wb_reg_case (OP2, 0, Val);		\ write_wb_reg_case (OP2, 1, VAL);		\ write_wb_reg_case (OP2, 2, VAL);		\ write_wb_reg_case (OP2, 3, VAL);		\ write_wb_reg_case (OP2, 4, VAL);		\ write_wb_reg_case (OP2, 5, VAL);		\ write_wb_reg_case (OP2, 6, VAL);		\ write_wb_reg_case (OP2, 7, VAL);		\ write_wb_reg_case (OP2, 8, VAL);		\ write_wb_reg_case (OP2, 9, VAL);	\ write_wb_reg_case (OP2, ten, VAL);	\ write_wb_reg_case (OP2, one, VAL);	\ write_wb_reg_case (OP2, VAL);	\ write_wb_reg_case (OP2, VAL);	\ write_wb_reg_case (OP2, +, VAL); \ write_wb_reg_case (OP2, N, Val) #define Write_wb_reg_case (OP2, M, Val) \ Case ((OP2 << 4) + M): \ Arm_dbg_write (C # # M, OP2, VAL); \ Break #define Arm_dbg_write (M, OP2, VAL) do {\ asm Volat Ile ("MCR p14, 0,%0, C0," #M "," #OP2:: "R" (VAL)); \}
 while (0)
After writing to the debug register, the breakpoint information is taken into effect.





In x86, by manipulating the breakpoint under the Dr Register.

In MIPS, there is also a dedicated debug register.



Not finished, back to add.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.