kernel hooks

The Kernel Hooks Interface is a generalised facility for placing hooks in arbitrary kernel locations.

A hook is a location in the kernel that calls out of the kernel to a kernel module routine - a hook exit routine. It enables many kernel enhancements, which are otherwise self-contained, to become loadable kernel modules and retain a substantial degree of independence from the kernel source.

Hooks are implemented as fast and slim-line insertions to kernel space code. When not active that have practically no overhead to the kernel.

LK init hook

在lk/top下除了main.c文件外还有一个init.c,实现了init hook功能。下面解析一下代码。

mian.c

在lk_main()中的函数有(按顺序排):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//在thread_init_early()和arch_early_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_ARCH_EARLY - 1);

//在arch_early_init()和platform_early_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH_EARLY, LK_INIT_LEVEL_PLATFORM_EARLY - 1);

//在platform_early_init()和target_early_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_LEVEL_TARGET_EARLY - 1);

//在target_early_init()和heap_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET_EARLY, LK_INIT_LEVEL_HEAP - 1);

//在heap_init()和kernel_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_HEAP, LK_INIT_LEVEL_KERNEL - 1);

//在kernel_init()和thread_create()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_KERNEL, LK_INIT_LEVEL_THREADING - 1);

在bootstrap2()中的函数有(按顺序排):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//在bootstrap2()函数刚进来,arch_init()之前:
lk_primary_cpu_init_level(LK_INIT_LEVEL_THREADING, LK_INIT_LEVEL_ARCH - 1);

//在arch_init()和platform_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH, LK_INIT_LEVEL_PLATFORM - 1);

//在platform_init()和target_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM, LK_INIT_LEVEL_TARGET - 1);

//在target_init()和apps_init()之间:
lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET, LK_INIT_LEVEL_APPS - 1);

//在apps_init()之后
lk_primary_cpu_init_level(LK_INIT_LEVEL_APPS, LK_INIT_LEVEL_LAST);

在lk_init_secondary_cpus()中的函数有(按顺序排):

1
lk_init_level(LK_INIT_FLAG_SECONDARY_CPUS, LK_INIT_LEVEL_THREADING, LK_INIT_LEVEL_LAST);

init.h

定义的枚举在init.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum lk_init_level {
LK_INIT_LEVEL_EARLIEST = 1,

LK_INIT_LEVEL_ARCH_EARLY = 0x10000,
LK_INIT_LEVEL_PLATFORM_EARLY = 0x20000,
LK_INIT_LEVEL_TARGET_EARLY = 0x30000,
LK_INIT_LEVEL_HEAP = 0x40000,
LK_INIT_LEVEL_VM = 0x50000,
LK_INIT_LEVEL_KERNEL = 0x60000,
LK_INIT_LEVEL_THREADING = 0x70000,
LK_INIT_LEVEL_ARCH = 0x80000,
LK_INIT_LEVEL_PLATFORM = 0x90000,
LK_INIT_LEVEL_TARGET = 0xa0000,
LK_INIT_LEVEL_APPS = 0xb0000,

LK_INIT_LEVEL_LAST = UINT_MAX,
};

enum lk_init_flags {
LK_INIT_FLAG_PRIMARY_CPU = 0x1,
LK_INIT_FLAG_SECONDARY_CPUS = 0x2,
LK_INIT_FLAG_ALL_CPUS = LK_INIT_FLAG_PRIMARY_CPU | LK_INIT_FLAG_SECONDARY_CPUS,
LK_INIT_FLAG_CPU_SUSPEND = 0x4,
LK_INIT_FLAG_CPU_RESUME = 0x8,
};

lk_primary_cpu_init_level等函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
typedef void (*lk_init_hook)(uint level); //函数类型的typedef,在下面的struct中用到,该函数接收一个uint类型的level参数,返回为空(void)。

void lk_init_level(enum lk_init_flags flags, uint start_level, uint stop_level);

static inline void lk_primary_cpu_init_level(uint start_level, uint stop_level)
{
lk_init_level(LK_INIT_FLAG_PRIMARY_CPU, start_level, stop_level);
} // 指定lk_init_flags flags为LK_INIT_FLAG_PRIMARY_CPU

static inline void lk_init_level_all(enum lk_init_flags flags)
{
lk_init_level(flags, LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_LAST);
} //init_level包含整个区间

struct lk_init_struct {
uint level; //init level
uint flags; // lk_init_flags flags
lk_init_hook hook; //指向的函数
const char *name; //hook name
};

#define LK_INIT_HOOK_FLAGS(_name, _hook, _level, _flags) \
const struct lk_init_struct _init_struct_##_name __ALIGNED(sizeof(void *)) __SECTION(".lk_init") = { \
.level = _level, \
.flags = _flags, \
.hook = _hook, \
.name = #_name, \
};

//该宏声明一个const型的lk_init_struct并对其进行初始化,宏传入4个参数(_name, _hook, _level, _flags)
//lk_init_struct 的名字为_init_struct_+'_name'
//_init_struct_+'_name'.level = _level
//_init_struct_+'_name'.flags = _flags
//_init_struct_+'_name'.hook = _hook
//_init_struct_+'_name'.name = '_name'

#define LK_INIT_HOOK(_name, _hook, _level) \
LK_INIT_HOOK_FLAGS(_name, _hook, _level, LK_INIT_FLAG_PRIMARY_CPU)
//该宏将LK_INIT_HOOK_FLAGS中的_flags定为LK_INIT_FLAG_PRIMARY_CPU

通过 __ALIGNED(sizeof(void *)) __SECTION(“.lk_init”)在编译器编译时,将各个地方LK_INIT_HOOK声明的lk_init_struct加入到.lk_init的数据段中。

__SECTION(“name”)是gcc编译器支持的一个编译特性(arm编译器也支持此特性),实现在编译时把某个函数/数据放到name的数据段中。

init.c

lk_init_level()函数在init.c中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
注意两个外部声明:
extern const struct lk_init_struct __lk_init[];
extern const struct lk_init_struct __lk_init_end[];

void lk_init_level(enum lk_init_flags required_flag, uint start_level, uint stop_level)
{
LTRACEF("flags %#x, start_level %#x, stop_level %#x\n",
required_flag, start_level, stop_level);

ASSERT(start_level > 0);
uint last_called_level = start_level - 1;
const struct lk_init_struct *last = NULL;
for (;;) {
/* search for the lowest uncalled hook to call */
LTRACEF("last %p, last_called_level %#x\n", last, last_called_level);

const struct lk_init_struct *found = NULL;
bool seen_last = false;
for (const struct lk_init_struct *ptr = __lk_init; ptr != __lk_init_end; ptr++) {
LTRACEF("looking at %p (%s) level %#x, flags %#x, seen_last %d\n", ptr, ptr->name, ptr->level, ptr->flags, seen_last);

if (ptr == last)
seen_last = true;

/* reject the easy ones */
if (!(ptr->flags & required_flag))
continue;
if (ptr->level > stop_level)
continue;
if (ptr->level < last_called_level)
continue;
if (found && found->level <= ptr->level)
continue;

/* keep the lowest one we haven't called yet */
if (ptr->level >= start_level && ptr->level > last_called_level) {
found = ptr;
continue;
}

/* if we're at the same level as the last one we called and we've
* already passed over it this time around, we can mark this one
* and early terminate the loop.
*/
if (ptr->level == last_called_level && ptr != last && seen_last) {
found = ptr;
break;
}
}

if (!found)
break;

#if TRACE_INIT
if (found->level >= EARLIEST_TRACE_LEVEL) {
printf("INIT: cpu %d, calling hook %p (%s) at level %#x, flags %#x\n",
arch_curr_cpu_num(), found->hook, found->name, found->level, found->flags);
}
#endif
found->hook(found->level);
last_called_level = found->level;
last = found;
}
}

lk_init_level()中主要为一个循环,从__lk_init到__lk_init_end之间遍历,找到符合条件的init hook去执行。

__lk_init 和 __lk_init_end 是 .lk_init数据段中定义的起始和结束,定义在lk/top/init.ld文件中:

1
2
3
4
5
6
7
8
SECTIONS {
.lk_init : {
__lk_init = .;
KEEP (*(.lk_init))
__lk_init_end = .;
}
}
INSERT AFTER .rodata;

使用init hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test_hook(uint level)
{
LTRACEF("level %#x\n", level);
}
LK_INIT_HOOK(test, test_hook, 1);


static void dpc_init(uint level)
{
event_init(&dpc_event, false, 0);

thread_detach_and_resume(thread_create("dpc", &dpc_thread_routine, NULL, DPC_PRIORITY, DEFAULT_STACK_SIZE));
}

LK_INIT_HOOK(libdpc, &dpc_init, LK_INIT_LEVEL_THREADING);

//lk中dpc thread就是以init hook的方式,在bootstrap2(void *arg)函数的开头创建的。

NOTE:
1.在编译好的lk.elf里查找.lk_init

1
aarch64-elf-readelf build-path/lk.elf -x .lk_init

2.查看一些debug信息

1
vim build-path/lk.elf.debug.lst 

3.查看system-onesegment信息

1
vim build-path/system-onesegment.ld