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))