* Srinivasa D S <[EMAIL PROTECTED]> [2008-04-16 11:59:39]: Hi Srinivasa,
Can you please break this patch into - The actual breakpoint assistance code. - single stepping out of line (ssol). - How to use breakpoint assistance + ssol. -- Thanks and Regards Srikar > Hi > > I have been working on providing breakpoint assistance(with SSOL) to > multithreaded applications using utrace infrastructure. To start with, I have > developed a small snippet of code which inserts/removes breakpoint at given > address and copies the original instruction to a separate vma. Whenever the > breakpoint is hit at the user specified address, user defined handler is > executed and single stepping is done on the original instruction present in > the separate vma(Single Step Out of Line, SSOL). After single stepping, we > fixup ip to get back to the instruction after the breakpoint. > > Breakpoint assistance feature can be exploited by clients of utrace like > uprobes. > > I have gone through Roland's mail(written last year) and subsequent > discussions on breakpoint assistance in systemtap mailinglist and found that > there are some concerns in using SSOL, like > > 1) Where to store out of line copies. > > SSOL copies are stored in a per-process SSOL area, which is a little VM area > created on each probed process's address space. Here in code, > setup_ssol_vma() > mmaps "n" bytes and then sets vmflags of the vma, so that ssol area is not > copied over fork and not resized with mremap. > > 2)Arch issues with instruction semantics. > > Once we single step on the original instruction, we need to fixup ip and > other > registers to allow it to proceed from next instruction after user specified > virtual address. > Since I have developed a minimal prototype which works on ppc, I have not > dealt with all types of instructions.(ofcourse, Iam not yet exposed to > instructions of x86/x86_64 instructions also). > post_ssol() verifies the single stepped instruction and updates the new ip > relative to the original instruction for a non-branch instruction. > For branch instruction(like b,bc) we update the nip, if the branch uses > relative addressing and update the link register to the instruction following > the original instruction address. > > > Implementation > > register_ssol() collects pid of the process, virtual address at which > breakpoint has to be inserted and handler to be executed before single > stepping on the original instruction. > > unregister_ssol() collects pid of the process, virtual address at which > breakpoint has to be removed. > > prerequisite > > All threads are quiesced and one of the thread makes a call to > ssol_register(). > > Iam posting this small piece of code with minimal feature to collect feedback > from the community before I develop a fully featured code. > So Please let me know your comments/views on my design and approach on > providing breakpoint assistance. > > > --- > arch/powerpc/kernel/Makefile | 1 > arch/powerpc/kernel/ssol_bkpt.c | 79 ++++++++ > include/linux/ssol_bkpt.h | 99 ++++++++++ > init/Kconfig | 9 > kernel/Makefile | 1 > kernel/ssol_bkpt.c | 386 > ++++++++++++++++++++++++++++++++++++++++ > kernel/utrace.c | 2 > 7 files changed, 576 insertions(+), 1 deletion(-) > > Index: linux-2.6.25-rc6/kernel/utrace.c > =================================================================== > --- linux-2.6.25-rc6.orig/kernel/utrace.c > +++ linux-2.6.25-rc6/kernel/utrace.c > @@ -439,7 +439,7 @@ dead: > } > > > -static struct utrace_attached_engine * > +struct utrace_attached_engine * > matching_engine(struct utrace *utrace, int flags, > const struct utrace_engine_ops *ops, void *data) > { > Index: linux-2.6.25-rc6/kernel/ssol_bkpt.c > =================================================================== > --- /dev/null > +++ linux-2.6.25-rc6/kernel/ssol_bkpt.c > @@ -0,0 +1,386 @@ > +/* > + * Insetion/Removal of breakpoint > + * kernel/ssol_bkpt.c > + * > + * 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) IBM Corporation, 2008 > + */ > +#include <linux/ssol_bkpt.h> > + > +static noinline unsigned long setup_ssol_vma(unsigned long nbytes) > +{ > + unsigned long addr; > + struct mm_struct *mm = current->mm; > + struct vm_area_struct *vma; > + > + BUG_ON(nbytes & ~PAGE_MASK); > + > + down_write(&mm->mmap_sem); > + /* > + * Find the end of the top mapping and skip a page. > + * If there is no space for PAGE_SIZE above > + * that, mmap will ignore our address hint. > + */ > + vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb); > + addr = vma->vm_end + PAGE_SIZE; > + addr = do_mmap_pgoff(NULL, addr, nbytes, PROT_EXEC, > + MAP_PRIVATE|MAP_ANONYMOUS, 0); > + if (addr & ~PAGE_MASK) { > + up_write(&mm->mmap_sem); > + printk(KERN_ERR "Breakpoint failed to allocate a vma for" > + "pid/tgid %d/%d for SSOL.\n", > + current->pid, current->tgid); > + return addr; > + } > + > + vma = find_vma(mm, addr); > + BUG_ON(!vma); > + /* avoid vma copy on fork() and don't expand when mremap() */ > + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; > + > + up_write(&mm->mmap_sem); > + return addr; > +} > + > +static noinline struct ssol_area *init_ssol(struct ssol_process *sproc) > +{ > + struct ssol_area *area = &sproc->sarea; > + struct ssol_slot *slot; > + int i; > + char *slot_addr; > + > + if (!sproc->sarea.initialized) { > + sproc->sarea.initialized = 1; > + area = (struct ssol_area *)kzalloc(sizeof(struct ssol_area), > + GFP_USER); > + area->insn_area = (opcode_t *) setup_ssol_vma(PAGE_SIZE); > + if (IS_ERR(area->insn_area)) > + return NULL; > + area->nfree = area->nslots = PAGE_SIZE / MAX_UINSN_BYTES; > + if (area->nslots > MAX_SSOL_SLOTS) > + area->nfree = area->nslots = MAX_SSOL_SLOTS; > + area->slots = (struct ssol_slot *) > + kzalloc(sizeof(struct ssol_slot) * area->nslots, > + GFP_USER); > + if (!area->slots) { > + area->insn_area = ERR_PTR(-ENOMEM); > + return NULL; > + } > + spin_lock_init(&area->lock); > + area->next_slot = 0; > + slot_addr = (char *) area->insn_area; > + for (i = 0; i < area->nslots; i++) { > + slot = &area->slots[i]; > + init_rwsem(&slot->rwsem); > + slot->owner = NULL; > + slot->last_used = 0; > + slot->insn = (__user opcode_t *) slot_addr; > + slot_addr += MAX_UINSN_BYTES; > + } > + return area; > + } else > + return &sproc->sarea; > +} > + > +static noinline int take_slot(struct ssol_area *area, struct probept *ppt) > +{ > + struct ssol_slot *s; > + struct ssol_slot *slot; > + int i, len; > + > + for (i = 0; i < area->nslots; i++) { > + slot = &area->slots[i]; > + if (slot->owner == NULL) > + break; > + } > + s = &area->slots[i]; > + ppt->slot = s; > + s->owner = ppt; > + len = access_process_vm(current, (unsigned long)s->insn, > + ppt->insn, MAX_UINSN_BYTES, 1); > + if (unlikely(len < MAX_UINSN_BYTES)) { > + printk(KERN_ERR "Failed to copy instruction at %#lx" > + " to SSOL area (%#lx)\n", ppt->u->vaddr, > + (unsigned long) area->slots); > + return -1; > + } > + return 0; > +} > + > +static struct probept *find_ppt(unsigned long vaddr, struct ssol_process > *sproc) > +{ > + struct hlist_node *node; > + struct hlist_head *head; > + struct probept *ppt; > + int i; > + > + for (i = 0; i < UPROBE_TABLE_SIZE; i++) { > + head = &sproc->ppt_table[i]; > + hlist_for_each_entry(ppt, node, head, ut_node) { > + if (ppt->u->vaddr == vaddr) > + return ppt; > + } > + } > + return NULL; > +} > + > +static u32 probe_report_signal(struct utrace_attached_engine *engine, > + struct task_struct *tsk, struct pt_regs *regs, u32 action, > + siginfo_t *info, const struct k_sigaction *orig_ka, > + struct k_sigaction *return_ka) > +{ > + struct ssol_area *area; > + struct probept *ppt; > + struct ssol_process *sproc; > + unsigned long vaddr; > + u32 ret; > + > + sproc = (struct ssol_process *)rcu_dereference(engine->data); > + vaddr = arch_get_probept(regs); > + > + if (info->si_signo == BREAKPOINT_SIGNAL) { > + switch (sproc->state) { > + case UPTASK_RUNNING: > + ppt = find_ppt(vaddr, sproc); > + if (!ppt) > + return UTRACE_ACTION_RESUME; > + area = init_ssol(sproc); > + if (!area) { > + printk(KERN_INFO > + "Failed to set the ssol area \n"); > + return UTRACE_ACTION_RESUME; > + } > + ret = take_slot(area, ppt); > + if (!ret) { > + ppt->u->handler(ppt->u, regs); > + pre_ssol(ppt, regs); > + sproc->state = UPTASK_SSTEP; > + sproc->active_probe = ppt; > + ret = UTRACE_ACTION_HIDE | UTRACE_SIGNAL_IGN > + | UTRACE_ACTION_SINGLESTEP > + | UTRACE_ACTION_NEWSTATE; > + } > + break; > + case UPTASK_SSTEP: > + ppt = sproc->active_probe; > + post_ssol(ppt, regs); > + sproc->state = UPTASK_RUNNING; > + sproc->active_probe = NULL; > + ret = UTRACE_ACTION_HIDE | UTRACE_SIGNAL_IGN > + | UTRACE_ACTION_NEWSTATE > + | UTRACE_ACTION_RESUME; > + break; > + default : > + ret = UTRACE_ACTION_RESUME; > + break; > + } > + return ret; > + } > + return UTRACE_ACTION_RESUME; > +} > + > +static const struct utrace_engine_ops bpt_utrace_ops = { > + .report_signal = probe_report_signal, > +}; > + > +static int validate_vaddr(struct task_struct *p, unsigned long vaddr) > +{ > + struct vm_area_struct *vma; > + struct mm_struct *mm = p->mm; > + if (!mm) > + return -EINVAL; > + down_read(&mm->mmap_sem); > + vma = find_vma(mm, vaddr); > + if (!vma || vaddr < vma->vm_start || !(vma->vm_flags & VM_EXEC)) { > + up_read(&mm->mmap_sem); > + return -EINVAL; > + } > + up_read(&mm->mmap_sem); > + return 0; > +} > + > +static struct ssol_process *ssol_find_task(struct task_struct *tsk) > +{ > + struct hlist_head *head; > + struct hlist_node *node; > + struct ssol_process *sproc; > + > + head = &sproc_table[hash_ptr(tsk, UPROBE_HASH_BITS)]; > + hlist_for_each_entry(sproc, node, head, hlist) { > + if (sproc->tsk == tsk) > + return sproc; > + } > + return NULL; > +} > + > +static struct ssol_process *ssol_mk_process(struct task_struct *p) > +{ > + struct ssol_process *sproc; > + int i; > + > + sproc = (struct ssol_process *)kzalloc(sizeof *sproc, GFP_USER); > + if (unlikely(sproc == NULL)) > + return ERR_PTR(-ENOMEM); > + /* Initialize fields */ > + for (i = 0; i < UPROBE_TABLE_SIZE; i++) > + INIT_HLIST_HEAD(&sproc->ppt_table[i]); > + INIT_HLIST_NODE(&sproc->hlist); > + sproc->tgid = p->tgid; > + sproc->tsk = p; > + sproc->sarea.insn_area = NULL; > + sproc->sarea.initialized = 0; > + sproc->state = UPTASK_RUNNING; > + hlist_add_head(&sproc->hlist, > + &sproc_table[hash_long(p, UPROBE_HASH_BITS)]); > + return sproc; > +} > + > +static struct probept *ssol_mk_ppt(struct ssol_user *u, > + struct ssol_process *sproc) > +{ > + struct probept *ppt; > + > + ppt = (struct probept *)kzalloc(sizeof *ppt, GFP_USER); > + if (unlikely(ppt == NULL)) > + return ERR_PTR(-ENOMEM); > + > + ppt->slot = NULL; > + ppt->u = u; > + INIT_HLIST_NODE(&ppt->ut_node); > + hlist_add_head(&ppt->ut_node, > + &sproc->ppt_table[hash_long(ppt->u->vaddr, UPROBE_HASH_BITS)]); > + return ppt; > +} > + > +static int set_bp(struct probept *ppt, struct task_struct *tsk) > +{ > + opcode_t bp_insn = BREAKPOINT_INSTRUCTION; > + return access_process_vm(tsk, ppt->u->vaddr, &bp_insn, BP_INSN_SIZE, 1); > +} > + > +void insert_bkpt(struct ssol_user *u, struct task_struct *p) > +{ > + struct utrace_attached_engine *engine = NULL; > + unsigned long newflags; > + int len; > + struct probept *ppt; > + struct ssol_process *sproc; > + > + sproc = ssol_find_task(p); > + if (!sproc) > + sproc = ssol_mk_process(p); > + ppt = find_ppt(u->vaddr, sproc); > + if (!ppt) > + ppt = ssol_mk_ppt(u, sproc); > + > + engine = utrace_attach(p, UTRACE_ATTACH_CREATE, > + &bpt_utrace_ops, sproc); > + if (IS_ERR(engine)) { > + long err = PTR_ERR(engine); > + printk(KERN_ERR "utrace_attach failed, returned %ld\n", > + err); > + return ; > + } > + newflags = engine->flags; > + newflags |= (UTRACE_EVENT(SIGNAL) | UTRACE_EVENT(SIGNAL_IGN) | > + UTRACE_EVENT(SIGNAL_CORE)); > + > + utrace_set_flags(p, engine, newflags); > + len = access_process_vm(p, ppt->u->vaddr, ppt->insn, MAX_UINSN_BYTES, > + 0); > + if (len < BP_INSN_SIZE) { > + printk(KERN_ERR "error reading original instruction %d\n", > + len); > + return; > + } > + memcpy(&ppt->opcode, ppt->insn, BP_INSN_SIZE); > + if (ppt->opcode == BREAKPOINT_INSTRUCTION) { > + printk(KERN_ERR "bkpt already exists at that addr\n"); > + return; > + } > + len = set_bp(ppt, p); > + if (len < BP_INSN_SIZE) { > + printk(KERN_ERR "failed to insert bkpt instruction\n"); > + return; > + } > + return; > +} > + > +int register_ssol(struct ssol_user *u) > +{ > + int ret; > + struct task_struct *p; > + > + if (!u || !u->handler) > + return -EINVAL; > + > + rcu_read_lock(); > + p = find_task_by_pid(u->pid); > + rcu_read_unlock(); > + if (!p) > + return -EINVAL; > + ret = validate_vaddr(p, u->vaddr); > + if (ret < 0) > + return -EINVAL; > + insert_bkpt(u, p); > + return 0; > +} > +EXPORT_SYMBOL(register_ssol); > + > +static int set_orig_insn(struct probept *ppt, struct task_struct *tsk) > +{ > + return access_process_vm(tsk, ppt->u->vaddr, &ppt->opcode, BP_INSN_SIZE, > + 1); > +} > + > +static void remove_bkpt(struct probept *ppt, struct task_struct *tsk) > +{ > + int len; > + > + if (tsk) { > + len = set_orig_insn(ppt, tsk); > + if (len < BP_INSN_SIZE) { > + printk(KERN_ERR > + "Failed to remove breakpoint at %#lx:", > + ppt->u->vaddr); > + } > + } > + hlist_del(&ppt->ut_node); > +} > + > +int unregister_ssol(struct ssol_user *u) > +{ > + struct task_struct *p; > + struct probept *ppt; > + struct ssol_process *sproc; > + > + if (!u) > + return -EINVAL; > + rcu_read_lock(); > + p = find_task_by_pid(u->pid); > + rcu_read_unlock(); > + if (!p) > + return -EINVAL; > + sproc = ssol_find_task(p); > + ppt = find_ppt(u->vaddr, sproc); > + if (!ppt) > + return -EINVAL; > + remove_bkpt(ppt, p); > + return 0; > +} > +EXPORT_SYMBOL(unregister_ssol); > + > +MODULE_LICENSE("GPL"); > Index: linux-2.6.25-rc6/include/linux/ssol_bkpt.h > =================================================================== > --- /dev/null > +++ linux-2.6.25-rc6/include/linux/ssol_bkpt.h > @@ -0,0 +1,99 @@ > +/* > + * Insertion/Removal of breakpoint > + * inlcude/linux/ssol_bkpt.h > + * > + * 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) IBM Corporation, 2006 > + */ > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/debugfs.h> > +#include <asm/uaccess.h> > +#include <linux/utrace.h> > +#include <linux/types.h> > +#include <linux/ptrace.h> > +#include <linux/signal.h> > +#include <linux/hash.h> > +#include <linux/pagemap.h> > +#include <linux/mm.h> > +#include <asm/cacheflush.h> > +#include <asm/mman.h> > + > +typedef unsigned int opcode_t; > +#define BREAKPOINT_INSTRUCTION 0x7fe00008 > +#define BP_INSN_SIZE 4 > +#define MAX_UINSN_BYTES 4 > +#define BREAKPOINT_SIGNAL SIGTRAP > + > +#define UPROBE_HASH_BITS 5 > +#define UPROBE_TABLE_SIZE (1 << UPROBE_HASH_BITS) > +#define MAX_SSOL_SLOTS 1024 > + > +struct ssol_area { > + __user opcode_t *insn_area; > + int nslots; > + int nfree; > + struct ssol_slot *slots; > + spinlock_t lock; > + int next_slot; > + int initialized; > +}; > + > +struct ssol_slot { > + __user opcode_t *insn; > + struct probept *owner; > + struct rw_semaphore rwsem; > + unsigned long last_used; > +}; > + > +struct ssol_user { > + pid_t pid; > + unsigned long vaddr; > + void (*handler) (struct ssol_user*, struct pt_regs*); > + void *kdata; > +}; > + > +enum ssol_task_state { > + UPTASK_RUNNING, > + UPTASK_SSTEP, > +}; > + > +struct probept { > + opcode_t opcode; > + enum ssol_task_state state; > + struct hlist_node ut_node; > + struct ssol_user *u; > + opcode_t insn[MAX_UINSN_BYTES / sizeof(opcode_t)]; > + struct ssol_slot *slot; > +}; > + > +struct ssol_process { > + struct hlist_head ppt_table[UPROBE_TABLE_SIZE]; > + struct hlist_node hlist; > + struct task_struct *tsk; > + struct ssol_area sarea; > + enum ssol_task_state state; > + pid_t tgid; > + struct probept *active_probe; > + struct ssol_area ssol_bkpt_area; > +}; > + > +void pre_ssol(struct probept *ppt, struct pt_regs *regs); > +void post_ssol(struct probept *ppt, struct pt_regs *regs); > +unsigned long arch_get_probept(struct pt_regs *regs); > +void calc_offset(struct probept *ppt, struct pt_regs *regs); > +static struct hlist_head sproc_table[UPROBE_TABLE_SIZE]; > Index: linux-2.6.25-rc6/arch/powerpc/kernel/ssol_bkpt.c > =================================================================== > --- /dev/null > +++ linux-2.6.25-rc6/arch/powerpc/kernel/ssol_bkpt.c > @@ -0,0 +1,79 @@ > +/* > + * Insetion/Removal of breakpoint > + * kernel/ssol_bkpt.h powerpc > + * > + * 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) IBM Corporation, 2008 > + */ > +#include <linux/ssol_bkpt.h> > + > +void pre_ssol(struct probept *ppt, struct pt_regs *regs) > +{ > + struct ssol_slot *slot; > + regs->nip = (long)ppt->slot->insn; > +} > + > +void calc_offset(struct probept *ppt, struct pt_regs *regs) > +{ > + int offset = 0; > + unsigned int opcode = 0; > + unsigned int insn = *ppt->insn; > + > + opcode = insn >> 26; > + switch (opcode) { > + case 16: /* bc */ > + if ((insn & 2) == 0) { > + offset = (signed short)(insn & 0xfffc); > + regs->nip = ppt->u->vaddr + offset; > + } > + if (insn & 1) > + regs->link = ppt->u->vaddr + MAX_UINSN_BYTES; > + break; > + case 18: /* b */ > + if ((insn & 2) == 0) { > + offset = insn & 0x03fffffc; > + if (offset & 0x02000000) > + offset -= 0x04000000; > + regs->nip = ppt->u->vaddr + offset; > + } > + if (insn & 1) > + regs->link = ppt->u->vaddr + MAX_UINSN_BYTES; > + break; > + } > + > + return; > +} > + > +void post_ssol(struct probept *ppt, struct pt_regs *regs) > +{ > + unsigned long copy_nip; > + > + copy_nip = (unsigned long) ppt->slot->insn; > + > + /* > + * If the single stepped instruction is non-branch instruction > + * then update the IP to be relative to probepoint. > + */ > + if (regs->nip == copy_nip + MAX_UINSN_BYTES) > + regs->nip = ppt->u->vaddr + MAX_UINSN_BYTES; > + else > + calc_offset(ppt, regs); > +} > + > +unsigned long arch_get_probept(struct pt_regs *regs) > +{ > + return (unsigned long)(regs->nip); > +} > Index: linux-2.6.25-rc6/arch/powerpc/kernel/Makefile > =================================================================== > --- linux-2.6.25-rc6.orig/arch/powerpc/kernel/Makefile > +++ linux-2.6.25-rc6/arch/powerpc/kernel/Makefile > @@ -66,6 +66,7 @@ obj-$(CONFIG_MODULES) += ppc_ksyms.o > obj-$(CONFIG_BOOTX_TEXT) += btext.o > obj-$(CONFIG_SMP) += smp.o > obj-$(CONFIG_KPROBES) += kprobes.o > +obj-$(CONFIG_SSOL) += ssol_bkpt.o > obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o > > pci64-$(CONFIG_PPC64) += pci_dn.o isa-bridge.o > Index: linux-2.6.25-rc6/init/Kconfig > =================================================================== > --- linux-2.6.25-rc6.orig/init/Kconfig > +++ linux-2.6.25-rc6/init/Kconfig > @@ -887,6 +887,15 @@ config UTRACE > applications. Unless you are making a specially stripped-down > kernel and are very sure you don't need these facilitiies, > say Y. > + > +config SSOL > + bool "Single stepping the virtual address of a process out of line" > + default y > + depends on UTRACE > + help > + This option provides single stepping the process's virtual address > out of > + line using utrace. It has interface for insertion and removal of > breakpoint. > + say Y/N here if you want to enable SSOL. > endmenu > > source "block/Kconfig" > Index: linux-2.6.25-rc6/kernel/Makefile > =================================================================== > --- linux-2.6.25-rc6.orig/kernel/Makefile > +++ linux-2.6.25-rc6/kernel/Makefile > @@ -53,6 +53,7 @@ obj-$(CONFIG_AUDIT) += audit.o auditfilt > obj-$(CONFIG_AUDITSYSCALL) += auditsc.o > obj-$(CONFIG_AUDIT_TREE) += audit_tree.o > obj-$(CONFIG_KPROBES) += kprobes.o > +obj-$(CONFIG_SSOL) += ssol_bkpt.o > obj-$(CONFIG_UTRACE) += utrace.o > obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o > obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ >