Perf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
tp_upgrade tftp 192.168.1.200 xdr3010mv6_cn_1_0_18_flash(231102)_perf.bin
tp_upgrade tftp 192.168.1.200 xdr3010mv6_cn_1_0_18_flash(231103)_origin_perf.bin
tp_upgrade firmware;

cd /tmp
tftp -gr usr_ax3000.tar.gz 192.168.1.200
tar -zxvf usr_ax3000.tar.gz
vim /etc/profile
# 添加下面两行
export LD_LIBRARY_PATH=/tmp/usr/lib:$LD_LIBRARY_PATH
export PATH=/tmp/usr/bin:$PATH

perf record -C 1 -g -F 99 sleep 10
perf record -a -g -F 99 sleep 10
# 在tmp目录下得到 perf.data,用script工具进行解析
perf script -i perf.data &> perf.unfold

tftp -pl perf.unfold 192.168.1.200
# ubuntu
FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
FlameGraph/flamegraph.pl perf.folded > perf.svg

tcpdump

1
2
3
4
5
6
7
8
9
10
11
12
13
# 指定设备(这里指定的是网桥br-lan)抓包,并将抓取的报文保存到文件(这里保存到br-lan.pcap)
cd /tmp
tftp -gr tcpdump 192.168.1.10
chmod 777 tcpdump
./tcpdump -i br-lan -w host.pcap
# 抓取完成后,使用ctrl c停止抓包,然后用tftp命令将抓取的报文导出
tftp -pl host.pcap 192.168.1.10
# 【注意】tcpdump的钩子在内核部分,在驱动的更上层,驱动收到报文后将其交到协议栈后才能在tcpdump抓到,如果驱动丢包,则tcpdump上抓不到
# 通过cmd & 后台运行
ping 192.168.1.1 &
# 通过杀ping进程结束运行
killall ping
kill -9 pid

addr2line

基本用法:addr2len 选项 地址

选项 描述
-a –address 显示地址
-b –target=设置二进位文件格式
-e –exe=设置输入文件名称(默认为a.out)
-i –inline 解开内联函数
-j –section=读取相对于段的偏移而非地址
-p –pretty-print 让输出对人类更可读
-s –basenames 去除目录名
-f –functions 显示函数名
-C –demangle[=style] 解码函数名
-h –help 显示帮助文本

使用流程

  1. 反汇编.o文件

    1
    2
    # /home/zengruihua/workspace/xdr3010mv6/SLP_SDK_for_xdr3010mv6_230917
    ./slp-sp-target-src/rtl/toolchain/msdk-10.3.0-mips-EB-5.10-g2.30-m32s-210630/bin/mips-linux-gnu-objdump -d ./build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_a4.o > /tmp/yxyx
  2. 找到对应的section

    1
    2
    vim /tmp/yxyx
    # 使用/找到对应函数

    也可以直接用readelf来查看elf的符号表,符号就是程序中的函数和变量,函数名或变量名就是符号名

    查看符号表

    readelf -s elf_file

    查看节信息

    readelf -S elf_file

  3. add2line指定section

    1
    2
    # 使用addr2line 指定 section
    ./slp-sp-target-src/rtl/toolchain/msdk-10.3.0-mips-EB-5.10-g2.30-m32s-210630/bin/mips-linux-gnu-addr2line -a -i -p -f -C -e ./build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_a4.o 0x118 -j .text.a4_recv_remote_handle

注意点

第一现场,即epc和最上面的函数调用,可以直接加偏移量

其他的函数要 -4,这是由于汇编代码会在调用函数时,使用LR暂存下一行指令,这里的下一行就是+4,然后函数调用执行完毕后,就把LR赋给PC,并继续执行。

log:

[   55.020102] CPU 1 Unable to handle kernel paging request at virtual address 00007f88, epc == c09951c8, ra == c09951c0
[   55.031743] Oops[#1]:
[   55.034236] CPU: 1 PID: 0 Comm: swapper/1 Tainted: G           O      5.10.70 #1
[   55.042327] $ 0   : 00000000 00000001 00000001 00000101
[   55.048057] $ 4   : 80940000 00000302 00000000 c1370288
[   55.053787] $ 8   : 00000010 802f1cec 00000000 00000000
[   55.059511] $12   : 00000000 82813c1d ffffffff 00000000
[   55.065232] $16   : c1abc000 00000000 84fe0000 82813e64
[   55.070950] $20   : c0a70000 00000000 c1510888 c0a6e450
[   55.076671] $24   : 00000000 80012d34
[   55.082389] $28   : 8288c000 82813e40 c1471000 c09951c0
[   55.088111] Hi    : 23626b04
[   55.091254] Lo    : 38fec800
[   55.095760] epc   : c09951c8 rtw_core_rx_process+0x9a8/0xa50 [rtk_wifi6]
[   55.104513] ra    : c09951c0 rtw_core_rx_process+0x9a0/0xa50 [rtk_wifi6]
[   55.111846] Status: 1100fc03 KERNEL EXL IE
[   55.116425] Cause : 4080000c (ExcCode 03)
[   55.120803] BadVA : 00007f88
[   55.123952] PrId  : 0001a120 (MIPS interAptiv (multi))
[   55.129572] Modules linked in: br_guest_filter(O) dns_proxy(O) dn_login(O) arp_filter(O) igmp_proxy_snooping(O) wfo_virt(O) rtk_wifi6(O) ther_ctrl(O) wan_port_detect_hook(O) map_filter(O) k_mwan_load_balance(O) game_port(O) slp_gpio(O) xt_multiurl(O) k_wlan_assoc_info(O) k_eventMngt(O)
[   55.157310] Process swapper/1 (pid: 0, threadinfo=5f40ec35, task=5857a7f6, tls=00000000)
[   55.166156] Stack : c0107ed4 800ba3a4 00000000 82813ee0 00000009 8093f360 808902b4 8089028c
[   55.175313]         c1510888 c1510888 00000000 80012448 00000004 813e0350 80cb0000 813e024c
[   55.184472]         00000000 80087398 c1510888 80cd0000 c0a674f0 c147660c c0990000 c09952ac
[   55.193630]         c0990000 c0a6b428 8093f360 00000001 8093f3cc fffdffff 8093f378 00020000
[   55.202787]         000003e8 00000000 c0114a18 c0107eb4 00000001 c010f000 84fe0000 00000001
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
[ 4232.224109] CPU 1 Unable to handle kernel paging request at virtual address c1b13830, epc == 800a52d4, ra == 800a5224
[ 4232.235733] Oops[#1]:
[ 4232.238212] CPU: 1 PID: 0 Comm: swapper/1 Tainted: G O 5.10.70 #1
[ 4232.246293] $ 0 : 00000000 00000001 c1b1382c 813de4c0
[ 4232.252020] $ 4 : 00000001 00000180 00000000 813dea08
[ 4232.257741] $ 8 : ffffffe0 00001000 82813ef4 00000004
[ 4232.263460] $12 : 813de4d8 00000001 00000200 82813ef8
[ 4232.269180] $16 : 80940000 813de480 3fffffff 8093e040
[ 4232.274899] $20 : 80ca1228 813de498 80890000 00000002
[ 4232.280619] $24 : 00000001 00000000
[ 4232.286339] $28 : 8288c000 82813ee0 00000020 800a5224
[ 4232.292058] Hi : 005f446b
[ 4232.295195] Lo : a0000000
[ 4232.298352] epc : 800a52d4 run_timer_softirq+0x124/0x38c
[ 4232.304349] ra : 800a5224 run_timer_softirq+0x74/0x38c
[ 4232.310246] Status: 1100fc02 KERNEL EXL
[ 4232.314532] Cause : 4080000c (ExcCode 03)
[ 4232.318912] BadVA : c1b13830
[ 4232.322049] PrId : 0001a120 (MIPS interAptiv (multi))
[ 4232.327664] Modules linked in: br_guest_filter(O) dns_proxy(O) dn_login(O) arp_filter(O) igmp_proxy_snooping(O) wfo_virt(O) rtk_wifi6(O) ther_ctrl(O) wan_port_detect_hook(O) map_filter(O) k_mwan_load_balance(O) game_port(O) slp_gpio(O) xt_multiurl(O) k_wlan_assoc_info(O) k_eventMngt(O)
[ 4232.355399] Process swapper/1 (pid: 0, threadinfo=69545d85, task=ccc7d431, tls=00000000)
[ 4232.364246] Stack : 80991f90 00000004 809b68a0 00000011 c1aa3a74 c1b1382c 00000000 00000000
[ 4232.373404] 00000004 80940000 00000001 00000001 00000001 80940000 809c0000 80892604
[ 4232.382560] 00000001 80947924 3fffffff 8093e040 80ca1228 80ca11c4 8093e044 00000101
[ 4232.391720] 00000020 807b4f38 00000000 00000000 00000000 00000000 00000004 00000004
[ 4232.400878] 80ca6aa0 0000000a 00060001 80940000 8088d57c 00200042 807bf050 0000001f

PC(Ubuntu):

1
2
3
4
5
6
# 方法1:
# 编出出来的模块文件.ko是一个普通的ELF文件,使用file和nm命令可以查看相关信息,使用nm可以查看模块目标文件的符号信息,地址,符号类型,符号名
$ nm ./build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/rtk_wifi6.ko | grep rtw_core_rx_process
0000d840 T rtw_core_rx_process
# 0x0000d840 + 0x9a8 = 0xe1e8
$ addr2line -a -i -p -f -C -e ./build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/rtk_wifi6.ko 0xe1e8
1
2
3
4
5
6
7
8
9
10
11
# 方法2:
$ nm ./build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_recv.o | grep rtw_core_rx_process
00000000 T rtw_core_rx_process
# 根据log得到
# 0 + 0x9a8 = 0x9a8 <= pc
# 0 + 0x9a0 = 0x9a0 <= lr/ra
$ addr2line -a -i -p -f -C -e ./build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_recv.o 0x9a8 x99c
0x000009a8: amsdu_to_msdu 于 /home/zengruihua/workspace/xdr3010mv6/SLP_SDK_for_xdr3010mv6_230917/build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_recv.c:3900

0x00000000: check_fwstate 于 /home/zengruihua/workspace/xdr3010mv6/SLP_SDK_for_xdr3010mv6_230917/build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_recv.c:3639
(已内连入) sta2sta_data_frame 于 /home/zengruihua/workspace/xdr3010mv6/SLP_SDK_for_xdr3010mv6_230917/build_dir/target-mips-linux-gnu/linux-rtl_rtl8198d/g6_wifi_driver_015.20/core/rtw_recv.c:1510

GBD

coredump

  1. 进入sdk的gdb目录:SLP_SDK_for_etr13080mtv1_221028/staging_dir/toolchain-aarch64/bin(也可能不是arch64的gdb)
  2. 将dms文件以及挂起时的dms.XX.core文件都放到gdb的目录下
  3. 启动gdb:./aarch64-openwrt-linux-musl-gdb dms dms.XX.core
  4. 加载符号表:set sysroot ../../target-aarch64/root-ipq95xx/
  5. bt

dms coredump

  1. 进入sdk的gdb目录:SLP_SDK_for_etr13080mtv1_221028/staging_dir/toolchain-aarch64/bin(也可能不是arch64的gdb)
  2. 将dms文件以及挂起时的dms.XX.core文件都放到gdb的目录下
  3. 启动gdb:./aarch64-openwrt-linux-musl-gdb dms dms.XX.core
  4. 加载符号表:
    set sysroot ../../target-aarch64/root-ipq95xx/
    MTK 14060
    set sysroot ../../target-aarch64_cortex-a53_musl/root-mediatek/
    hostapd?
    set solib-search-path ../../../staging_dir/toolchain-arm_cortex-a7_gcc-10.2.0_musl_eabi/lib
  5. bt

readelf

readelf -h

使用命令 readelf -h 可以查看一个 ELF 文件的 ELF Header 信息。

比较重要的成员有:e_ident(ELF 文件幻数)、e_type(比如可执行文件 ET_EXEC)、e_entry(程序入口虚拟地址)等等。

ELF 头部定义如以下结构体所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* /include/uapi/linux/elf.h */
typedef struct elf32_hdr
{
unsigned char e_ident [EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;

简单归纳各字段

1
2
3
4
5
6
7
elf header:
magic num、version、arch、endian、flag、elf header size
elf type: EXEC (Executable file)、REL (Relocatable file)、DYN (Shared object file)
entry point: EXEC 文件才有,程序入口虚拟地址
program hdr offset/size/num in file:
section hdr offset/size/num in file:
str table of section hdr idx:

readelf -S

使用命令 readelf -S 可以查看一个 ELF 文件的 Section Header 信息。

一个 ELF 文件中到底有哪些具体的 sections,由包含在这个 ELF 文件中的 section head table (SHT) 决定。每个 section 描述了这个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其它属性。

下面介绍下常见和比较重要的 section:

sh_name sh_type description
.text SHT_PROGBITS 代码段,包含程序的可执行指令
.data SHT_PROGBITS 包含初始化了的数据,将出现在程序的内存映像中
.bss SHT_NOBITS 未初始化数据
.rodata SHT_PROGBITS 包含只读数据
.comment SHT_PROGBITS 包含版本控制信息
.eh_frame SHT_PROGBITS 它生成描述如何 unwind 堆栈的表
.debug SHT_PROGBITS 此节区包含用于符号调试的信息
.dynsym SHT_DYNSYM 此节区包含了动态链接符号表
.shstrtab SHT_STRTAB 存放 section 名,字符串表。Section Header String Table
.strtab SHT_STRTAB 字符串表
.symtab SHT_SYMTAB 符号表
.got SHT_PROGBITS 全局偏移表
.plt SHT_PROGBITS 过程链接表
.relname SHT_REL 包含了重定位信息,例如 .text 节区的重定位节区名字将是:.rel.text

Section 头部定义如以下结构体所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* /include/uapi/linux/elf.h */
typedef struct elf32_shdr {
Elf32_Word sh_name; // 节区名,名字是一个 NULL 结尾的字符串。
Elf32_Word sh_type; // 为节区类型
Elf32_Word sh_flags; // 节区标志
Elf32_Addr sh_addr; // 节区的第一个字节应处的位置。否则,此字段为 0。
Elf32_Off sh_offset; // 此成员的取值给出节区的第一个字节与文件头之间的偏移。
Elf32_Word sh_size; // 此成员给出节区的长度(字节数)。
Elf32_Word sh_link; // 此成员给出节区头部表索引链接。其具体的解释依赖于节区类型。
Elf32_Word sh_info; // 此成员给出附加信息,其解释依赖于节区类型。
Elf32_Word sh_addralign; // 某些节区带有地址对齐约束.
Elf32_Word sh_entsize; // 给出每个表项的长度字节数。
} Elf32_Shdr;

简单归纳各字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
section header: (用于 link 的 elf 必须有,其他文件不是必要的)
name(string tbl index)、offset、size、addr
type:
PROGBITS
REL:重定位,如.rel.text
NOBITS:
STRTAB:字符串表,格式为 str1 \0 str2 \0 ... (其他使用该 str 时不需要记录 size)
SYMTAB:格式

flag: write、alloc、execute、merge、strings、info、exclude、group

.rel.text:
Offset Info Type Sym.Value Sym. Name
00000038 00000e04 R_MIPS_26 00000000 b_func_1

.symtab:
val:取决 type,可能是地址(相对所在 section 的 offset)
type 有 SECTION OBJECT(变量) FUNC NOTYPE(外部 sym) FILE(文件名 a.c)
Ndx: ABS (文件名) UND(外部 sym)其他为所在 section index
Bind:LOCAL GLOBAL(外部可见)
例子:
Num: Value Size Type Bind Vis Ndx Name
13: 00000000 200 FUNC GLOBAL DEFAULT 1 a_func_1
14: 00000000 0 NOTYPE GLOBAL DEFAULT UND b_func_1

readelf -I

使用命令 readelf -l 可以查看一个 ELF 文件的 Program Header 信息。

程序头是一个结构的数组,每一个结构都表示一个段 (segments)。在可执行文件或者共享链接库中所有的节 (sections) 都被分为不同的几个段 (segments)。

程序头的索引地址 (e_phoff)、段数量 (e_phnum)、表项大小 (e_phentsize) 都是通过 ELF 头部信息获取的。

Program 头部定义如以下结构体所示:

1
2
3
4
5
6
7
8
9
10
11
/* /include/uapi/linux/elf.h */
typedef struct elf32_phdr {
Elf32_Word p_type; /* Magic number and other info */
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;

简单归纳各字段

1
2
3
4
5
6
7
8
9
10
11
12
Program Headers:(用于进程 img 加载,其他文件不是必要的)
例子:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
REGINFO 0x000094 0x00400094 0x00400094 0x00018 0x00018 R 0x4
LOAD 0x000000 0x00400000 0x00400000 0x003a0 0x003a0 R E 0x10000
LOAD 0x0003a0 0x004103a0 0x004103a0 0x00020 0x00050 RW 0x10000

Section to Segment mapping:
Segment Sections...
00 .reginfo
01 .reginfo .text
02 .data .sbss .bss

从加载的角度来看,ELF 文件被分成了许多段,ELF 文件中的代码,链接信息和注释都以段的形式存访。每个段在程序表头表中有一个描述项,分别包含段的类型,段的驻留位置相对于 ELF 文件开始处的偏移量,段在内存中的首地址,段的物理地址,段在文件中的大小,段在内存中的大小,段的对齐标志,如上图所示。

一个可执行文件至少要有一个可加载类型的段,这种类型的段会被装载或映射进内存中,这个会在后面分析程序加载流程时会描述到。