如果想要查看内核函数地址,查一查内核符号表就行了,可是想要知道某个函数内语句相对于函数起始地址的偏移还是需要一定方法。
需求:想要在以下函数struct sk_buff *next = skb->next;
处插桩,如何获取到对应的offset。
3253 struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
3254 struct netdev_queue *txq, int *ret)
3255 {
3256 struct sk_buff *skb = first;
3257 int rc = NETDEV_TX_OK;
3258
3259 while (skb) {
3260 struct sk_buff *next = skb->next;
3261
3262 skb->next = NULL;
3263 rc = xmit_one(skb, dev, txq, next != NULL);
3264 if (unlikely(!dev_xmit_complete(rc))) {
3265 skb->next = next;
3266 goto out;
3267 }
3268
3269 skb = next;
3270 if (netif_xmit_stopped(txq) && skb) {
3271 rc = NETDEV_TX_BUSY;
3272 break;
3273 }
3274 }
3275
3276 out:
3277 *ret = rc;
3278 return skb;
3279 }
- 方法一:使用crash
dis -l $func_name
命令获取
环境搭建可参考 Linux crash 调试环境搭建
[root@gitclient kernel-4.18.0-80.el8]# crash /usr/lib/debug/usr/lib/modules/4.18.0-80.el8.x86_64/vmlinux /proc/kcore
crash 7.2.9-2.el8
Copyright (C) 2002-2020 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions.
This program has absolutely no warranty. Enter "help warranty" for details.
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu"...
WARNING: kernel relocated [706MB]: patching 91326 gdb minimal_symbol values
KERNEL: /usr/lib/debug/usr/lib/modules/4.18.0-80.el8.x86_64/vmlinux
DUMPFILE: /proc/kcore
CPUS: 8
DATE: Mon Mar 20 23:11:11 EDT 2023
UPTIME: 1 days, 00:32:21
LOAD AVERAGE: 0.38, 0.14, 0.10
TASKS: 585
NODENAME: gitclient
RELEASE: 4.18.0-80.el8.x86_64
VERSION: #1 SMP Tue Jun 4 09:19:46 UTC 2019
MACHINE: x86_64 (2200 Mhz)
MEMORY: 16 GB
PID: 464226
COMMAND: "crash"
TASK: ffff99691c43ad00 [THREAD_INFO: ffff99691c43ad00]
CPU: 3
STATE: TASK_RUNNING (ACTIVE)
crash> dis -s dev_hard_start_xmit
crash> help dis
NAME
dis - disassemble
SYNOPSIS
dis [-rfludxs][-b [num]] [address | symbol | (expression)] [count]
DESCRIPTION
This command disassembles source code instructions starting (or ending) at
a text address that may be expressed by value, symbol or expression:
-r (reverse) displays all instructions from the start of the
routine up to and including the designated address.
-f (forward) displays all instructions from the given address
to the end of the routine.
-l displays source code line number data in addition to the
disassembly output.
-u address is a user virtual address in the current context;
otherwise the address is assumed to be a kernel virtual address.
If this option is used, then -r and -l are ignored.
-x override default output format with hexadecimal format.
-d override default output format with decimal format.
-s displays the filename and line number of the source code that
is associated with the specified text location, followed by a
source code listing if it is available on the host machine.
The line associated with the text location will be marked with
an asterisk; depending upon gdb's internal "listsize" variable,
several lines will precede the marked location. If a "count"
argument is entered, it specifies the number of source code
lines to be displayed after the marked location; otherwise
the remaining source code of the containing function will be
displayed.
-b [num] modify the pre-calculated number of encoded bytes to skip after
a kernel BUG ("ud2a") instruction; with no argument, displays
the current number of bytes being skipped. (x86 and x86_64 only)
address starting hexadecimal text address.
symbol symbol of starting text address. On ppc64, the symbol
preceded by '.' is used.
(expression) expression evaluating to a starting text address.
count the number of instructions to be disassembled (default is 1).
If no count argument is entered, and the starting address
is entered as a text symbol, then the whole routine will be
disassembled. The count argument is supported when used with
the -r and -f options.
EXAMPLES
Disassemble the sys_signal() routine without, and then with, line numbers:
crash> dis sys_signal
0xc0112c88 <sys_signal>: push %ebp
0xc0112c89 <sys_signal+1>: mov %esp,%ebp
crash> dis -s dev_hard_start_xmit
FILE: net/core/dev.c
LINE: 3255
3250 return rc;
3251 }
3252
3253 struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
3254 struct netdev_queue *txq, int *ret)
* 3255 {
3256 struct sk_buff *skb = first;
3257 int rc = NETDEV_TX_OK;
3258
3259 while (skb) {
3260 struct sk_buff *next = skb->next;
3261
3262 skb->next = NULL;
3263 rc = xmit_one(skb, dev, txq, next != NULL);
3264 if (unlikely(!dev_xmit_complete(rc))) {
3265 skb->next = next;
3266 goto out;
3267 }
3268
3269 skb = next;
3270 if (netif_xmit_stopped(txq) && skb) {
3271 rc = NETDEV_TX_BUSY;
3272 break;
3273 }
3274 }
3275
3276 out:
3277 *ret = rc;
3278 return skb;
3279 }
crash> dis -l dev_hard_start_xmit
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3255
0xffffffffad8ca510 <dev_hard_start_xmit>: data32 data32 data32 xchg %ax,%ax [FTRACE NOP]
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3259
0xffffffffad8ca515 <dev_hard_start_xmit+5>: push %r15
0xffffffffad8ca517 <dev_hard_start_xmit+7>: push %r14
0xffffffffad8ca519 <dev_hard_start_xmit+9>: push %r13
0xffffffffad8ca51b <dev_hard_start_xmit+11>: push %r12
0xffffffffad8ca51d <dev_hard_start_xmit+13>: push %rbp
0xffffffffad8ca51e <dev_hard_start_xmit+14>: push %rbx
0xffffffffad8ca51f <dev_hard_start_xmit+15>: sub $0x20,%rsp
0xffffffffad8ca523 <dev_hard_start_xmit+19>: mov %rcx,0x18(%rsp)
0xffffffffad8ca528 <dev_hard_start_xmit+24>: test %rdi,%rdi
0xffffffffad8ca52b <dev_hard_start_xmit+27>: je 0xffffffffad8ca706 <dev_hard_start_xmit+502>
0xffffffffad8ca531 <dev_hard_start_xmit+33>: lea 0x90(%rsi),%rax
0xffffffffad8ca538 <dev_hard_start_xmit+40>: mov %rsi,%rbx
0xffffffffad8ca53b <dev_hard_start_xmit+43>: mov %rdi,%r15
0xffffffffad8ca53e <dev_hard_start_xmit+46>: mov %rdx,%r13
0xffffffffad8ca541 <dev_hard_start_xmit+49>: mov %rax,0x8(%rsp)
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3260
0xffffffffad8ca546 <dev_hard_start_xmit+54>: mov (%r15),%r12
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3262
0xffffffffad8ca549 <dev_hard_start_xmit+57>: mov $0xffffffffae609090,%rax
0xffffffffad8ca550 <dev_hard_start_xmit+64>: movq $0x0,(%r15)
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3263
0xffffffffad8ca557 <dev_hard_start_xmit+71>: mov (%rax),%rax
0xffffffffad8ca55a <dev_hard_start_xmit+74>: test %r12,%r12
0xffffffffad8ca55d <dev_hard_start_xmit+77>: setne %bpl
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/./include/linux/list.h: 203
0xffffffffad8ca561 <dev_hard_start_xmit+81>: cmp $0xffffffffae609090,%rax
0xffffffffad8ca567 <dev_hard_start_xmit+87>: je 0xffffffffad8ca6db <dev_hard_start_xmit+459>
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3243
0xffffffffad8ca56d <dev_hard_start_xmit+93>: mov %rbx,%rsi
0xffffffffad8ca570 <dev_hard_start_xmit+96>: mov %r15,%rdi
0xffffffffad8ca573 <dev_hard_start_xmit+99>: callq 0xffffffffad8c7fe0 <dev_queue_xmit_nit>
......
使用dis -s可以查看内核函数源码,dis -l 可以获取汇编代码对应源码的行号,如下所示便是 struct sk_buff *next = skb->next;
对应的偏移0x54
。
0xffffffffad8ca546 <dev_hard_start_xmit+54>: mov (%r15),%r12
/usr/src/debug/kernel-4.18.0-80.el8/linux-4.18.0-80.el8.x86_64/net/core/dev.c: 3262
-
方法二:使用内核驱动dump出函数内存,通过反汇编的方式获取
Linux 驱动读取当前内核中代码段数据 -
方法三:使用objdump反汇编整个 vmlinux,从而找到对应偏移位置
objdump -d -l vmlinux