本系列是 x86 架构平台上 trampoline 的实现,从原理和实现上进行了详细的介绍。
perilogue
是个缝合单词,由 prologue
和 epilogue
缝合组成。
而 prologue
和 epilogue
是 函数调用规约 中的术语。
prologue
: 函数整体指令中最开始的几条指令。
epilogue
: 函数整体指令中最末尾的几条指令。
perilogue
: 函数整体指令中最开始、及最末尾的几条指令。
leave
: 该指令的效果相当于 mov %ebp, %esp
+ pop %ebp
。
ret
: 该指令的效果相当于 pop %eip
。
想要深入研究 perilogue
的小司机们,推荐阅读以下两篇博客:
perilogue of kernel function
使用 eBPF Talk: tailcall on x86【汇编慎入】【译】 中查看内核函数汇编的方法,直接看看内核函数的汇编吧。
以比较简单的内核函数 eth_type_trans()
为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# cat /proc/kallsyms|grep eth_type_trans
ffffffff9eda5590 T __pfx_eth_type_trans
ffffffff9eda55a0 T eth_type_trans
ffffffff9fab5538 r __ksymtab_eth_type_trans
# gdb -q -c /proc/kcore -ex 'x/180i 0xffffffff9eda55a0' -ex 'quit'
[New process 1]
Core was generated by `BOOT_IMAGE=/vmlinuz-6.2.0-060200rc8-generic root=/dev/mapper/ubuntu--vg-ubuntu-'.
#0 0x0000000000000000 in ?? ()
0xffffffff9eda55a0: nopl 0x0(%rax,%rax,1)
0xffffffff9eda55a5: push %rbp
0xffffffff9eda55a6: mov %rsi,%rax
0xffffffff9eda55a9: mov %rsp,%rbp
0xffffffff9eda55ac: sub $0x10,%rsp
# ...
0xffffffff9eda5641: jne 0xffffffff9eda56fd
0xffffffff9eda5647: leave
0xffffffff9eda5648: xor %edx,%edx
0xffffffff9eda564a: xor %ecx,%ecx
0xffffffff9eda564c: xor %esi,%esi
0xffffffff9eda564e: xor %edi,%edi
0xffffffff9eda5650: xor %r8d,%r8d
0xffffffff9eda5653: ret
|
perilogue of bpf2bpf
以 eBPF Talk: bpf2bpf 特性揭秘 中的例子为例。
bpftool
工具提供了查看 JIT 后的 bpf prog 的程序汇编。在将 bpf prog 运行起来后:
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
|
# bpftool p
// ...
263: kprobe name k_tcp_connect tag d45d3e8749eeb769 gpl
loaded_at 2023-02-20T14:07:53+0000 uid 0
xlated 432B jited 246B memlock 4096B map_ids 13
btf_id 129
# bpftool p d j i 263 > bpftool.prog.dump.jit.log
# cat bpftool.prog.dump.jit.log
void handle_new_connection(void * ctx, struct sock * sk):
bpf_prog_93a5a8fde5afab51_handle_new_connection:
; handle_new_connection(void *ctx, struct sock *sk)
0: nopl (%rax,%rax)
5: nop
7: pushq %rbp
8: movq %rsp, %rbp
b: subq $24, %rsp
12: pushq %rbx
13: pushq %r13
15: pushq %r14
17: movq %rsi, %r14
1a: movq %rdi, %rbx
1d: xorl %edi, %edi
; ...
; }
d5: popq %r14
d7: popq %r13
d9: popq %rbx
da: leave
db: retq
dc: int3
|
P.S. 完整的 log 文件请查看 bpftool.prog.dump.jit.log。
总结
简要介绍了 x86 上的 perilogue
。
只需要记住:
prologue
: 函数整体指令中最开始的几条指令。
epilogue
: 函数整体指令中最末尾的几条指令。
因为将 eBPF Talk: poke on x86 作用在 prologue
的时候,威力无穷。