Linux kernel debugging techniques used to achieve --kretprobe

Excerpt: https://blog.csdn.net/luckyapple1028/article/details/54782659
first two Bowen describes the use and implementation of kprobes detection technology in kprobe and jprobe of. This article describes kretprobe kprobes last detection technique in which also based kprobe achieved, the probe can be used to return the value of the function and the function to execute time consuming calculations. This paper describes the use kretprobe through a simple example program, then by source code analysis of how it is implemented.

Kernel Source: Linux-4.1.x

Experimental environment: Fedora25 (x86_64), raspberry pie 1b

 

. 1, an example of using kretprobe
use kretprobe probe with the return value as jprobe need to write kernel module, of course, the kernel also provides a simple example program kretprobe_example.c (located sample / kprobes directory), to achieve the more general program, the user can be specified when using the parameters of the function module directly through to be detected, it returns the value of the detection function is also counted, when executed by the probe function. Before analyzing kretprobe_example.c kretprobe familiarize yourself with the structure definitions and API interfaces.

1.1、kretprobe结构体与API介绍
/*
* Function-return probe -
* Note:
* User needs to provide a handler function, and initialize maxactive.
* maxactive - The maximum number of instances of the probed function that
* can be active concurrently.
* nmissed - tracks the number of times the probed function's return was
* ignored, due to maxactive being too low.
*
*/
struct kretprobe {
struct kprobe kp;
kretprobe_handler_t handler;
kretprobe_handler_t entry_handler;
int maxactive;
int nmissed;
size_t data_size;
struct hlist_head free_instances;
raw_spinlock_t lock;
};
struct kretprobe structure is used to define a kretprobe. Because of its Based kprobe, natural and ultimately structural body kprobe field; handler and then entry_handler represent two callback functions, user defined, entry_handler is called probe function before being executed, the function handler returns are detected after call (usually print the return value is the probe function in this function); maxactive expressed support both limit parallel probe, as kretprobe keeps track of a function from start to finish, so for some calls more frequently detected function, in the detection of period of relatively high probability of re-entry, this maxactive field value, said in an reentrant happens, support the maximum number of simultaneous detection process (the number of execution flow), the number of parallel triggered if more than the upper limit, not kretprobe track detection, nmissed only increase the value of the field as a reminder; data_size field indicates the size of the kretprobe private data when registering kretprobe based on the size of the reserved space; and finally free_instances indicates free kretprobe running instance of the list, which links this kretprobe idle Examples of the structure represented struct kretprobe_instance.
kretprobe_instance {struct
struct hlist_node hList;
struct kretprobe * RP;
kprobe_opcode_t * ret_addr;
struct the task_struct * Task;
char Data [0];
};
This structure represents a running instance of kretprobe, said before the probe function may be complicated by the presence of the phenomenon performed during tracking, so kretprobe use a kretprobe_instance to track the implementation of a stream, the upper limit of support for the maxactive. When no trigger detection, all of the instances are stored in free_instances kretprobe_instance table, whenever a trigger kretprobe execution flow probe, will remove an idle kretprobe_instance instance from the table is used to track.
rp kretprobe_instance pointer points to the structure mentioned kretprobe belongs; ret_addr detected original function for holding the return address (see the text will be detected function return address is temporarily replaced); Task binding a process for its track; kretprobe private data last data saved by the user, it will be passed between entry_handler and callback handler during the entire run kretprobe detection (typically used to achieve statistical detected time-consuming function).

1.2, and demonstrates an example kretprobe_example analysis
is performed Processed kretprobe_example.c sample probe do_fork default program provided by the kernel function and the return value, the detection function is supported by the specified parameter module, the user need to detect if other functions only need to load the kernel module function name passed their own to be detected without the need to modify module code.

static char func_name[NAME_MAX] = "do_fork";
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"
" function's execution time");
下面详细分析:
/* per-instance private data */
struct my_data {
ktime_t entry_stamp;
};

static struct kretprobe my_kretprobe = {
.handler = ret_handler,
.entry_handler = entry_handler,
.data_size = sizeof(struct my_data),
/* Probe up to 20 instances concurrently. */
.maxactive = 20,
};

static int __init kretprobe_init(void)
{
int ret;

my_kretprobe.kp.symbol_name = func_name;
ret = register_kretprobe(&my_kretprobe);
if (ret < 0) {
printk(KERN_INFO "register_kretprobe failed, returned %d\n",
ret);
return -1;
}
printk(KERN_INFO "Planted return probe at %s: %p\n",
my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
return 0;
}

static void __exit kretprobe_exit(void)
{
unregister_kretprobe(&my_kretprobe);
printk(KERN_INFO "kretprobe at %p unregistered\n",
my_kretprobe.kp.addr);

/* nmissed > 0 suggests that maxactive was set too low. */
printk(KERN_INFO "Missed probing %d instances of %s\n",
my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
}
Program defines a structure struct my_data, wherein the only parameter for entry_stamp time calculation function to execute; while the program defines a struct kretprobe example, note that the private data length is my_data wherein the length of the maximum number of parallel probe 20 is supported (that is, if at some point, the number of execution flow do_fork function calls at the same time that more than 20 will no longer detect trace). Finally, init and exit function modules only call register_kretprobe and unregister_kretprobe function my_kretprobe register and log off, start sounding the default kretprobe after registration is complete.
/ * Here WE entry_hanlder to use The timestamp entry function * /
static int entry_handler (struct kretprobe_instance RI *, struct pt_regs * regs)
{
struct * Data my_data;

IF (! To current-> mm)
return. 1; / * Skip Kernel Threads * /

Data = (struct my_data *) RI-> Data;
DATA-> entry_stamp ktime_get = ();
return 0;
}

/ *
* the return-Probe Handler:. The return value and the Log On May Turn the Duration DURATION
* out to be zero consistently, depending upon the granularity of time
* accounting on the platform.
*/
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
int retval = regs_return_value(regs);
struct my_data *data = (struct my_data *)ri->data;
s64 delta;
ktime_t now;

now = ktime_get();
delta = ktime_to_ns(ktime_sub(now, data->entry_stamp));
printk(KERN_INFO "%s returned %d and took %lld ns to execute\n",
func_name, retval, (long long)delta);
return 0;
}
Entry_handler trigger function is called when the function is called do_fork, note that the first parameter is not the struct kretprobe structure, but represent a probe structure struct kretprobe_instance example, which is allocated from free_instances kretprobe list, the finished track in this process after the trigger recycling. entry_handler functions take advantage of kretprobe_instance private data, saving time start do_fork function to execute.
Function ret_handler complete do_fork function is executed after the return call, which subtracts the private data stored kretprobe_instance start time based on the current time, we can calculate the time-consuming do_fork function to perform. At the same time it calls regs_return_value function to get do_fork return value of the function, the function is related to architecture:

Long regs_return_value inline static (struct pt_regs * regs)
{
return regs-> ARM_r0;
}
example environment is the transfer arm by the return value of the register r0, thus achieving the function simply returns the value of the register r0 (Note that the pointer is passed regs when the function returns do_fork saved register information, it will analyze later). ret_handler function return value and print out the last time-consuming (in ns) do_fork function.
The following example demonstrates the procedure in x86_64 environment, the actual results (Environment Configuration Please "--kprobe use and implementation of the Linux kernel debugging techniques" reference):

<6>[ 1217.859349] _do_fork returned 1838 and took 518081 ns to execute
<6>[ 1217.863880] _do_fork returned 1839 and took 223701 ns to execute
<6>[ 1217.865731] _do_fork returned 1840 and took 221746 ns to execute
<6>[ 1220.077508] _do_fork returned 1841 and took 433573 ns to execute
<6>[ 1220.081512] _do_fork returned 1842 and took 362684 ns to execute
<6>[ 1220.083767] _do_fork returned 1843 and took 284184 ns to execute
<6>[ 1220.995537] _do_fork returned 1844 and took 503414 ns to execute
<6>[ 1221.000363] _do_fork returned 1845 and took 427427 ns to execute

Loading kretprobe_example.ko, the probe function is not specified, the default function do_fork probe, core outputs more messages. Visible do_fork function's return value (newly created process pid) is in ascending order, while also showing a function of time with a very intuitive execution. Therefore, kretprobe can get a simple function returns a value at the time of execution, it is useful when debugging kernel. Other detection methods similar functions will not be repeated.

 

2, kretprobe achieve analysis
kretprobe based implementation kprobe, so there will be a basis to analyze its blog post "Linux --kprobe use and implementation of technology Kernel Debugger" on the front, including kretprobe registration process and trigger detection process, kprobe relates portion not described in detail.

2.1, kretprobe implementation principle
with jprobe Similarly, kretprobe is a special form of kprobe, it has its own private pre_handler, it does not support user-defined pre_handler and post_handler such as callback functions. Where it's pre_handler callback function to return a value kretprobe probe function performed preparatory work, the most important is to replace the normal process of the return address, so that the probe function after executing the function can jump to the well-designed kretprobe go, it gets the return value of the function, and then call kretprobe-> handler callback function (the return address is the moment to obtain the output of the probe function), and finally return to normal execution flow.

2.2, a register kretprobe
kretprobe register_kretprobe detection module calls a kernel function to register kretprobe example, the code path kernel / kprobes.c, the main flow of FIG follows:

 

Figure 1 kretprobe registration process

int register_kretprobe (struct kretprobe * RP)
{
int RET = 0;
struct kretprobe_instance * Inst;
int I;
void * addr;

IF (kretprobe_blacklist_size) {
addr = kprobe_addr (& rp-> KP);
IF (IS_ERR (addr))
return PTR_ERR (addr);

for (I = 0; kretprobe_blacklist [I] .name = NULL;! I ++) {
IF (kretprobe_blacklist [I] .addr == addr)
return -EINVAL;
}
}
beginning of the function peculiar first treated kretprobe blacklist, if the specified function is detected directly in the blacklist in return EINVAL, said they did not support the probe. Wherein kretprobe_blacklist_size represents the queue length, kretprobe_blacklist is a global array of structures, each structure is a kretprobe_blackpoint struct:
struct {kretprobe_blackpoint
const char * name;
void * addr;
};
Where name field indicates the function name, addr represents the address of the function to run. This architecture is related kretprobe_blacklist for the stated architecture which function is not supported kretprobe probe, wherein the arm structure is not defined, the following schema is defined x86_64:
struct kretprobe_blackpoint kretprobe_blacklist [] = {
{ "__switch_to",} , / * this function only Switches Current Task, But
does Kernel Stack * not Switch /.
{NULL, NULL} / * Terminator * /
};
this indicates __switch_to function in kretprobe x86_64 architecture may not be detected (which a little kernel of kprobes.txt are already described). Back register_kretprobe function, comparing the detection function runs in a blacklist address addr field, which is initialized in the initialization function init_kprobes in kprobes subsystem:
IF (kretprobe_blacklist_size) {
/ * The Lookup function name address from ITS * /
for (I 0 =; kretprobe_blacklist [I] .name = NULL;! I ++) {
kprobe_lookup_name (kretprobe_blacklist [I] .name,
kretprobe_blacklist [I] .addr);
IF (kretprobe_blacklist [I] .addr!)
the printk ( "kretprobe: Lookup failed:% S \ n-",
kretprobe_blacklist [I] .name);
}
}
continue down registration function register_kretprobe Analysis:
rp-> kp.pre_handler = pre_handler_kretprobe ;
rp-> kp.post_handler = NULL;
rp-> kp.fault_handler = NULL;
rp-> kp.break_handler = NULL;

/ * max PreS-kretprobe instances for the allocate Memory * /
IF (rp-> maxActive <= 0) {
#ifdef CONFIG_PREEMPT is
rp-> max_t maxActive = (unsigned int, 10, 2 * num_possible_cpus ());
#else
rp-> num_possible_cpus maxActive = ();
#endif
}
where only the specified kprobe pre_handler callback function pre_handler_kretprobe; then if the user does not specify the maximum number of concurrent detection maxactive, there will be calculated and set a default value.
raw_spin_lock_init (& rp-> Lock);
INIT_HLIST_HEAD (& rp-> free_instances);
for (I = 0; I <rp-> maxActive; I ++) {
Inst = kmalloc (the sizeof (struct kretprobe_instance) +
rp-> Data_Size, GFP_KERNEL);
IF (Inst == NULL) {
free_rp_inst (RP);
return -ENOMEM;
}
INIT_HLIST_NODE (& inst-> hList);
hlist_add_head (& inst-> hList, & rp-> free_instances);
}

rp-> nmissed = 0;
/ * the Establish * Point Probe entry function /
RET = register_kprobe (& rp-> KP);
IF (RET = 0!)
free_rp_inst (RP);
return RET;
}
Next, according to the value of maxactive, and allocate memory for each instance they are linked to kretprobe_instance free_instances kretprobe list, the last function call register_kprobe registered embedded kprobe.
These are kretprobe registration process, we can see it, like jprobe is also very simple, is still ultimately depends kprobe mechanism.

 

2.3, triggering kretprobe detection
based kprobe mechanism, after executing the specified function is detected, triggers the abnormal CPU, kprobe probe into the process. First called by kprobe_handler pre_handler callback function, here pre_handler_kretprobe function, which first find a free kretprobe_instance probe instance and bind it with the current process, and then calls entry_handler callback function, then save and replace the return address detection function Finally, kprobe detection process ends and return to normal execution process execution is detected function that returns will be replaced kretprobe_trampoline jump to the function, which will get register information detected function and call the user-defined callback function in which output the return value of the last function returns the normal flow of execution.

 

Figure 2 kretprobe trigger process

/*
* This kprobe pre_handler is registered with every kretprobe. When probe
* hits it will set up the return probe.
*/
static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
{
struct kretprobe *rp = container_of(p, struct kretprobe, kp);
unsigned long hash, flags = 0;
struct kretprobe_instance *ri;

/*
* To avoid deadlocks, prohibit return probing in NMI contexts,
* just skip the probe and increase the (inexact) 'nmissed'
* statistical counter, so that the user is informed that
* something happened:
*/
if (unlikely(in_nmi())) {
rp->nmissed++;
return 0;
}

/* TODO: consider to only swap the RA after the last pre_handler fired */
hash = hash_ptr(current, KPROBE_HASH_BITS);
raw_spin_lock_irqsave(&rp->lock, flags);
if (!hlist_empty(&rp->free_instances)) {
ri = hlist_entry(rp->free_instances.first,
struct kretprobe_instance, hlist);
hlist_del(&ri->hlist);
raw_spin_unlock_irqrestore(&rp->lock, flags);

ri->rp = rp;
ri->task = current;

if (rp->entry_handler && rp->entry_handler(ri, regs)) {
raw_spin_lock_irqsave(&rp->lock, flags);
hlist_add_head(&ri->hlist, &rp->free_instances);
raw_spin_unlock_irqrestore(&rp->lock, flags);
return 0;
}

arch_prepare_kretprobe(ri, regs);

? / * XXX (HCH): Why IS there NO hlist_move_head * /
INIT_HLIST_NODE (& RI-> hList);
kretprobe_table_lock (hash, & flags);
hlist_add_head (& RI-> hList, & kretprobe_inst_table [hash]);
kretprobe_table_unlock (hash, & flags);
} the else {
rp-> nmissed ++;
raw_spin_unlock_irqrestore (& rp-> Lock, the flags);
}
return 0;
}
first descriptor address and KPROBE_HASH_BITS value calculated hash index value based on the current process, if kretprobe the free_instances list is not empty, then kretprobe_instance to find an instance of idle, then rp and task assignment field which represents the current process to detect instances and binding; entry_handler then calls the callback function (entry_handler function previously kretprobe_example sample program is called here); then arch_prepare_kretprobe down call function, the correlation function architecture for storing the return address and the replacement of regs, wherein the arm architecture implemented as follows:
void __kprobes arch_prepare_kretprobe (struct kretprobe_instance * RI,
struct pt_regs *regs)
{
ri-> ret_addr = (kprobe_opcode_t *) regs-> ARM_lr;

. / * The return the Replace with Trampoline addr * addr /
regs-> ARM_lr = (unsigned Long) & kretprobe_trampoline;
}
here regs-> ARM_lr saved to ri-> ret_addr then the original value of the address of the replaced become kretprobe_trampoline function. Meaning Note regs-> ARM_lr flow original code values is the next instruction to be invoked after the probe function addresses (since regs is performed in point register value is stored upon detection function entry instruction, the contents of register lr return address of the function execution is detected), after the replacement, the original implementation process in the implementation of a full probe function will be to jump to kretprobe_trampoline function is executed, the entire function analysis later. In view of the function implemented x86_64 architecture:
void arch_prepare_kretprobe (struct kretprobe_instance RI *, struct regs pt_regs *)
{
unsigned Long * = stack_addr Sara (regs);

RI-> ret_addr = (kprobe_opcode_t *) * Sara;

/ * The return the Replace Trampoline with addr * addr /
* Sara = (unsigned Long) & kretprobe_trampoline;
}
Overall very much the same function x86_64 architectures with different arm of the call stack, it returns the address stored in the stack space (ie, the position pointed to by sp), therefore saving and alternative ways with arm architecture is slightly different.
Continue back pre_handler_kretprobe function, the next will kretprobe_instance used to link this global kretprobe_inst_table hash table, the hash table is initialized in init_kprobes in. Finally, if the list is empty kretprobe of free_instances, then the detection function is triggered in parallel flow maxactive exceeds the upper limit specified value is not only increased nmissed detection and tracking.

After pre_handler_kretprobe function returns, the process then executes the kprobe singlestep flow and return to the normal flow of execution, the function is detected (do_fork) continues to execute until it completes and returns. Since the return address is replaced kretprobe_trampoline, so execution jumps to kretprobe_trampoline, the function architecture relevant and embedded assembly implemented, specific analysis.

1) arm architecture to achieve:

/*
* When a retprobed function returns, trampoline_handler() is called,
* calling the kretprobe's handler. We construct a struct pt_regs to
* give a view of registers r0-r11 to the user return-handler. This is
* not a complete pt_regs structure, but that should be plenty sufficient
* for kretprobe handlers which should normally be interested in r0 only
* anyway.
*/
void __naked __kprobes kretprobe_trampoline(void)
{
__asm__ __volatile__ (
"stmdb sp!, {r0 - r11} \n\t"
"mov r0, sp \n\t"
"bl trampoline_handler \n\t"
"mov lr, r0 \n\t"
"ldmia sp!, {r0 - r11} \n\t"
#ifdef CONFIG_THUMB2_KERNEL
"bx lr \n\t"
#else
"MOV PC, LR \ n-\ T"
#endif
::: "Memory");
}
This function constructs a stack space pt_regs incomplete structure variables, filled only register r0 ~ r11 (of interest due kretprobe is only a function return values r0, it was enough), then jumps to perform the function trampoline_handler:
/ * from the Called kretprobe_trampoline * /
static void * __used __kprobes trampoline_handler (struct pt_regs * regs)
{
struct kretprobe_instance * RI = NULL;
struct hlist_head * head, empty_rp;
struct hlist_node * tmp;
unsigned Long the flags, orig_ret_address = 0;
unsigned Long trampoline_address = (unsigned Long) & kretprobe_trampoline;

INIT_HLIST_HEAD (& empty_rp);
kretprobe_hash_lock (Current, & head, & the flags);

/ *
* It IS Possible to have have multiple instances associated with a given
* task either because multiple functions in the call path have
* a return probe installed on them, and/or more than one return
* probe was registered for a target function.
*
* We can handle this because:
* - instances are always inserted at the head of the list
* - when multiple return probes are registered for the same
* function, the first instance's ret_addr will point to the
* real return address, and all the rest will point to
* kretprobe_trampoline
*/
hlist_for_each_entry_safe(ri, tmp, head, hlist) {
if (ri->task != current)
/* another task is sharing our hash bucket */
continue;

if (ri->rp && ri->rp->handler) {
__this_cpu_write(current_kprobe, &ri->rp->kp);
get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
ri->rp->handler(ri, regs);
__this_cpu_write(current_kprobe, NULL);
}

orig_ret_address = (unsigned long)ri->ret_addr;
recycle_rp_inst(ri, &empty_rp);

if (orig_ret_address != trampoline_address)
/*
* This is the real return address. Any other
* instances associated with this task are for
* other calls deeper on the call stack
*/
break;
}

kretprobe_assert(ri, orig_ret_address, trampoline_address);
kretprobe_hash_unlock(current, &flags);

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del (& RI-> hList);
kfree (RI);
}

return (void *) orig_ret_address;
}
For the foregoing kprobe execution process has been completely pulled out, so there can not be obtained by means of parameter passing in the end is which triggered kretprobe_instance, can only be determined by kretprobe_instance preceding examples descriptor pointer kretprobe_inst_table global hash table and current processes. So function first traverse kretprobe_inst_table hash table, find kretprobe_instance and the current process is bound. We will temporarily modify the values found after state current_kprobe and kprobe, indicating that entered the process flow kprobe prevent conflict. Handler then calls the callback function, passing the second reference function is constructed in front of pt_regs kretprobe_trampoline, attention register r0 stored therein is the return value of the function.
After the handler callback function is finished, the current kretprobe_instance instance calls recycle_rp_inst function frees from kretprobe_inst_table hash table, re-links free_instances in, in case you later kretprobe trigger, in addition kretprobe has been canceled if it is added to the destruction of the table to be destroyed:

recycle_rp_inst void (struct kretprobe_instance RI *,
struct * hlist_head head)
{
struct * RP = RI- kretprobe> RP;

/ * Remove RP Inst The rprobe_inst_table OFF * /
hlist_del (& RI-> hList);
INIT_HLIST_NODE (& RI-> hList);
IF (LIKELY (RP)) {
raw_spin_lock (& rp-> Lock);
hlist_add_head (& RI-> hList, & rp-> free_instances);
raw_spin_unlock (& rp-> Lock);
} the else
/ * Unregistering * /
hlist_add_head (& RI-> hList , head);
}
return to trampoline_handler function, then there is a case to be noted that due to the global hash table when traversing method adopted here when looking kretprobe_instance, while there may be multiple instances binding with the current process kretprobe case, the call flow as a function is detected may call other functions to be detected, for example, the following scenario:
int B (void)
{
int RET;

...

Return the right;
}

Int a (void)
{
int RET;

RET = b ();
...

return RET;
}
If the function of a and b function also registered kretprobe, multi kretprobe_instance will bind the same process occurs. In the case of this multi-binding, you may find kretprobe_instance instance is bound to a function of errors in processing b function return values, leading to detection errors. So how to avoid this error? In fact, explanation has been given in the notes. Is used here in a very clever way, each time the first table is inserted into the head insertion kretprobe_inst_table, acquired at the time the head is withdrawn, similar to a stack, followed by a break in the final condition given cycle, that is, if original function's return address is not equal kretprobe_trampoline address of the function, it would break, no longer circulating Find a kretprobe_instance instance. We know that under normal circumstances this will inevitably break condition is satisfied, so here is necessarily find examples of the last probe trigger kretprobe the process.
Back trampoline_handler function to traverse kretprobe_instance last instance empty_rp destruction to be released. Finally, return to the original function's return address is detected, the flow of execution to return to kretprobe_trampoline function:

"MOV LR, r0 \ n-\ T"
"LDMIA SP !, {r0 - R11} \ n-\ T"
#ifdef CONFIG_THUMB2_KERNEL
"BX LR \ n-\ T"
#else
"MOV PC, LR
next removed from the register r0 the original return address, and then restore the original function call stack space, and finally jump to the original implementation of the return address, thus function call to return to the normal flow of the process, the end of the whole kretprobe detection.

2)x86_64架构实现
/*
* When a retprobed function returns, this code saves registers and
* calls trampoline_handler() runs, which calls the kretprobe's handler.
*/
static void __used kretprobe_trampoline_holder(void)
{
asm volatile (
".global kretprobe_trampoline\n"
"kretprobe_trampoline: \n"
#ifdef CONFIG_X86_64
/* We don't bother saving the ss register */
" pushq %rsp\n"
" pushfq\n"
SAVE_REGS_STRING
" movq %rsp, %rdi\n"
" call trampoline_handler\n"
/* Replace saved sp with true return address. */
" movq %rax, 152(%rsp)\n"
RESTORE_REGS_STRING
" popfq\n"
#else
"PUSHF \ n-"
SAVE_REGS_STRING
"Movl% ESP,% EAX \ n-"
"Call trampoline_handler \ n-"
/ * the Move the flags to CS * /
"Movl 56 is (% ESP),% EDX \ n-"
"Movl% EDX, 52 is ( ESP%) \ n-"
/ * the Replace to true return address saved with the flags * /.
" Movl% EAX, 56 is (ESP%) \ n-"
RESTORE_REGS_STRING
" POPF \ n-"
#endif
" RET \ n-");
}
realization of the principle It is consistent with the arm, calls SAVE_REGS_STRING here to register onto the stack, constructed pt_regs variable, and then call trampoline_handler function that basically the same arm of the same, is not posted, the last kretprobe_trampoline_holder restore the original stack space and return address jump the normal execution flow to continue.

3 summarizes
kretprobe kprobe based detection technology implemented, is the last kprobes detection techniques, the return value of the kernel developer can dynamically performed by its detection function, and it may be some customization of actions, such as detecting the function execution time, easy to use. This article describes the use of tools and principles kretprobe detection, source code analysis and through its implementation under the arm architecture and x86_64 architectures.

Finally, together with the former two blog posts a more detailed analysis of the three functions kprobes detection technology, flexible use of these three debugging techniques can greatly improve the efficiency of kernel development and positioning problem.
----------------
Disclaimer: This article is the original article CSDN bloggers "luckyapple1028", and follow CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement. .
Original link: https: //blog.csdn.net/luckyapple1028/java/article/details/54782659

Guess you like

Origin www.cnblogs.com/LiuYanYGZ/p/12643846.html
Recommended