diff mbox

[v2,05/13] uprobes: add arch write opcode hook

Message ID 1381871068-27660-6-git-send-email-dave.long@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

David Long Oct. 15, 2013, 9:04 p.m. UTC
From: Rabin Vincent <rabin@rab.in>

Allow arches to write the opcode with a custom function.  ARM needs to
customize the swbp instruction depending on the condition code of the
instruction it replaces.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  3 +++
 kernel/events/uprobes.c | 21 ++++++++++++++++-----
 2 files changed, 19 insertions(+), 5 deletions(-)

Comments

Oleg Nesterov Oct. 19, 2013, 4:50 p.m. UTC | #1
On 10/15, David Long wrote:
>
> Allow arches to write the opcode with a custom function.  ARM needs to
> customize the swbp instruction depending on the condition code of the
> instruction it replaces.

Well, we already have "__weak set_swbp(auprobe, ...)", can't arm use it?

If not,

> +void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
> +				     uprobe_opcode_t opcode)
> +{
> +	memcpy(vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
> +}
> ...
> -	copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
> +	vaddr_new = kmap_atomic(new_page);
> +	arch_uprobe_write_opcode(auprobe, vaddr_new + (vaddr & ~PAGE_MASK),
> +				 opcode);
> +	kunmap_atomic(vaddr_new);

Again, if you need to add the new __weak helper, I think it should simply
do copy_to_page().

Oleg.
David Long Oct. 23, 2013, 6:20 p.m. UTC | #2
On 10/19/13 12:50, Oleg Nesterov wrote:
> On 10/15, David Long wrote:
>>
>> Allow arches to write the opcode with a custom function.  ARM needs to
>> customize the swbp instruction depending on the condition code of the
>> instruction it replaces.
>
> Well, we already have "__weak set_swbp(auprobe, ...)", can't arm use it?
>
> If not,
>
>> +void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
>> +				     uprobe_opcode_t opcode)
>> +{
>> +	memcpy(vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
>> +}
>> ...
>> -	copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
>> +	vaddr_new = kmap_atomic(new_page);
>> +	arch_uprobe_write_opcode(auprobe, vaddr_new + (vaddr & ~PAGE_MASK),
>> +				 opcode);
>> +	kunmap_atomic(vaddr_new);
>
> Again, if you need to add the new __weak helper, I think it should simply
> do copy_to_page().
>
> Oleg.
>

Unfortunately, providing an alternative set_swbp() would mean 
duplicating a moderate chunk of code from kernel/uprobes.c.  Adding 
arch_uprobe_write_opcode() feels a little more like the right thing to 
do to me.  I notice there don't seem to be any alternative set_swbp 
functions in the (rc6) kernel tree.  I will adjust the 
arch_uprobe_write_opcode imlementation as suggested.

-dl
diff mbox

Patch

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index a28dcbe..34375da 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -136,6 +136,9 @@  extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
 extern int __weak arch_uprobes_init(void);
+extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
+					    void *vaddr,
+					    uprobe_opcode_t opcode);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 9734a5f..cb01d82 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -248,6 +248,12 @@  static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * have fixed length instructions.
  */
 
+void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
+				     uprobe_opcode_t opcode)
+{
+	memcpy(vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
+}
+
 /*
  * write_opcode - write the opcode at a given virtual address.
  * @mm: the probed process address space.
@@ -260,11 +266,12 @@  static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * For mm @mm, write the opcode at @vaddr.
  * Return 0 (success) or a negative errno.
  */
-static int write_opcode(struct mm_struct *mm, unsigned long vaddr,
-			uprobe_opcode_t opcode)
+static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
+			unsigned long vaddr, uprobe_opcode_t opcode)
 {
 	struct page *old_page, *new_page;
 	struct vm_area_struct *vma;
+	void *vaddr_new;
 	int ret;
 
 retry:
@@ -285,7 +292,10 @@  retry:
 	__SetPageUptodate(new_page);
 
 	copy_highpage(new_page, old_page);
-	copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
+	vaddr_new = kmap_atomic(new_page);
+	arch_uprobe_write_opcode(auprobe, vaddr_new + (vaddr & ~PAGE_MASK),
+				 opcode);
+	kunmap_atomic(vaddr_new);
 
 	ret = anon_vma_prepare(vma);
 	if (ret)
@@ -314,7 +324,7 @@  put_old:
  */
 int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-	return write_opcode(mm, vaddr, UPROBE_SWBP_INSN);
+	return write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN);
 }
 
 /**
@@ -329,7 +339,8 @@  int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
 int __weak
 set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-	return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
+	return write_opcode(auprobe, mm, vaddr,
+			    *(uprobe_opcode_t *)auprobe->insn);
 }
 
 static int match_uprobe(struct uprobe *l, struct uprobe *r)