func_ptr_is_kernel_text()
kernel/extable.c
/* * On some architectures (PPC64, IA64) function pointers * are actually only tokens to some data that then holds the * real function address. As a result, to find if a function * pointer is part of the kernel text, we need to do some * special dereferencing first. */ int func_ptr_is_kernel_text(void *ptr) { unsigned long addr; addr = (unsigned long) dereference_function_descriptor(ptr); if (core_kernel_text(addr)) return 1; return is_module_text_address(addr); }
ptr 주소가 커널 코드 또는 모듈 코드 영역인지 여부를 알아온다.
__kernel_text_address()
kernel/extable.c
int __kernel_text_address(unsigned long addr) { if (core_kernel_text(addr)) return 1; if (is_module_text_address(addr)) return 1; if (is_ftrace_trampoline(addr)) return 1; /* * There might be init symbols in saved stacktraces. * Give those symbols a chance to be printed in * backtraces (such as lockdep traces). * * Since we are after the module-symbols check, there's * no danger of address overlap: */ if (init_kernel_text(addr)) return 1; return 0; }
addr 주소가 core 커널 코드, 모듈 코드, ftrace 코드, init 커널 코드 영역인 경우 1을 리턴하고 그렇지 않으면 0을 리턴한다.
core_kernel_text()
kernel/extable.c
int core_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr < (unsigned long)_etext) return 1; if (system_state == SYSTEM_BOOTING && init_kernel_text(addr)) return 1; return 0; }
addr 주소가 _stext ~ _etext 범위에 있거나 부팅 중 init kernel 코드 영역인 경우 1을 리턴하고 그렇지 않으면 0을 리턴한다.
core_kernel_data()
kernel/extable.c
/** * core_kernel_data - tell if addr points to kernel data * @addr: address to test * * Returns true if @addr passed in is from the core kernel data * section. * * Note: On some archs it may return true for core RODATA, and false * for others. But will always be true for core RW data. */ int core_kernel_data(unsigned long addr) { if (addr >= (unsigned long)_sdata && addr < (unsigned long)_edata) return 1; return 0; }
addr 주소가 _sdata ~ _edata 영역에 포함되어 있는지 여부를 리턴한다.
init_kernel_text()
kernel/extable.c
static inline int init_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_sinittext && addr < (unsigned long)_einittext) return 1; return 0; }
addr 주소가 _sinittext ~ _einittext 영역에 포함되어 있는지 여부를 리턴한다.
is_module_text_address()
kernel/module.c
/* * is_module_text_address - is this address inside module code? * @addr: the address to check. * * See is_module_address() if you simply want to see if the address is * anywhere in a module. See kernel_text_address() for testing if an * address corresponds to kernel or module code. */ bool is_module_text_address(unsigned long addr) { bool ret; preempt_disable(); ret = __module_text_address(addr) != NULL; preempt_enable(); return ret; }
addr 주소가 모듈 코드 영역에 포함되어 있는지 여부를 알아온다.
__module_text_address()
kernel/module.c
/* * __module_text_address - get the module whose code contains an address. * @addr: the address. * * Must be called with preempt disabled or module mutex held so that * module doesn't get freed during this. */ struct module *__module_text_address(unsigned long addr) { struct module *mod = __module_address(addr); if (mod) { /* Make sure it's within the text section. */ if (!within(addr, mod->module_init, mod->init_text_size) && !within(addr, mod->module_core, mod->core_text_size)) mod = NULL; } return mod; } EXPORT_SYMBOL_GPL(__module_text_address);
addr 주소가 모듈 초기화 코드 및 모듈 코어 코드에 포함되어 있는지 여부를 알아온다.
__module_address()
kernel/module.c
/* * __module_address - get the module which contains an address. * @addr: the address. * * Must be called with preempt disabled or module mutex held so that * module doesn't get freed during this. */ struct module *__module_address(unsigned long addr) { struct module *mod; if (addr < module_addr_min || addr > module_addr_max) return NULL; list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; if (within_module(addr, mod)) return mod; } return NULL; } EXPORT_SYMBOL_GPL(__module_address);
addr 주소가 module_addr_min ~ module_addr_max에 포함되어 있고 로드된 모듈의 코드영역에 있는 경우 module 포인터를 리턴하고 그렇지 않으면 null을 리턴한다.
within_module()
include/linux/module.h
static inline bool within_module(unsigned long addr, const struct module *mod) { return within_module_init(addr, mod) || within_module_core(addr, mod); }
addr 주소가 지정된 모듈의 초기화 코드 또는 코어 코드에 포함되어 있는지 여부를 리턴한다.
within_module_init()
include/linux/module.h
static inline bool within_module_init(unsigned long addr, const struct module *mod) { return (unsigned long)mod->module_init <= addr && addr < (unsigned long)mod->module_init + mod->init_size; }
addr 주소가 지정된 모듈의 초기화 코드 영역에 포함되어 있는지 여부를 리턴한다.
is_ftrace_trampoline()
kernel/trace/ftrace.c
/* * This is used by __kernel_text_address() to return true if the * address is on a dynamically allocated trampoline that would * not return true for either core_kernel_text() or * is_module_text_address(). */ bool is_ftrace_trampoline(unsigned long addr) { struct ftrace_ops *op; bool ret = false; /* * Some of the ops may be dynamically allocated, * they are freed after a synchronize_sched(). */ preempt_disable_notrace(); do_for_each_ftrace_op(op, ftrace_ops_list) { /* * This is to check for dynamically allocated trampolines. * Trampolines that are in kernel text will have * core_kernel_text() return true. */ if (op->trampoline && op->trampoline_size) if (addr >= op->trampoline && addr < op->trampoline + op->trampoline_size) { ret = true; goto out; } } while_for_each_ftrace_op(op); out: preempt_enable_notrace(); return ret; }
addr 주소가 각 ftrace_ops_list를 뒤져 각 op 구조체의 영역에 포함되어 있는지 여부를 알아내어 리턴한다.
do_for_each_ftrace_op()
kernel/trace/ftrace.c
/* * Traverse the ftrace_global_list, invoking all entries. The reason that we * can use rcu_dereference_raw_notrace() is that elements removed from this list * are simply leaked, so there is no need to interact with a grace-period * mechanism. The rcu_dereference_raw_notrace() calls are needed to handle * concurrent insertions into the ftrace_global_list. * * Silly Alpha and silly pointer-speculation compiler optimizations! */ #define do_for_each_ftrace_op(op, list) \ op = rcu_dereference_raw_notrace(list); \ do
while_for_each_ftrace_op()
kernel/trace/ftrace.c
/* * Optimized for just a single item in the list (as that is the normal case). */ #define while_for_each_ftrace_op(op) \ while (likely(op = rcu_dereference_raw_notrace((op)->next)) && \ unlikely((op) != &ftrace_list_end))