嵌入式调测工具 发表于 2026-01-18 | 更新于 2026-02-01
| 浏览量:
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 /tmptftp -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 perf script -i perf.data &> perf.unfold tftp -pl perf.unfold 192.168.1.200 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 cd /tmptftp -gr tcpdump 192.168.1.10 chmod 777 tcpdump./tcpdump -i br-lan -w host.pcap tftp -pl host.pcap 192.168.1.10 ping 192.168.1.1 & 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 显示帮助文本
使用流程
反汇编.o文件
1 2 ./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
找到对应的section
也可以直接用readelf来查看elf的符号表,符号就是程序中的函数和变量,函数名或变量名就是符号名
查看符号表
readelf -s elf_file
查看节信息
readelf -S elf_file
add2line指定section
1 2 ./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 $ 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 $ 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 $ 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 $ 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
进入sdk的gdb目录:SLP_SDK_for_etr13080mtv1_221028/staging_dir/toolchain-aarch64/bin(也可能不是arch64的gdb)
将dms文件以及挂起时的dms.XX.core文件都放到gdb的目录下
启动gdb:./aarch64-openwrt-linux-musl-gdb dms dms.XX.core
加载符号表:set sysroot ../../target-aarch64/root-ipq95xx/
bt
dms coredump
进入sdk的gdb目录:SLP_SDK_for_etr13080mtv1_221028/staging_dir/toolchain-aarch64/bin(也可能不是arch64的gdb)
将dms文件以及挂起时的dms.XX.core文件都放到gdb的目录下
启动gdb:./aarch64-openwrt-linux-musl-gdb dms dms.XX.core
加载符号表: 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
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 文件开始处的偏移量,段在内存中的首地址,段的物理地址,段在文件中的大小,段在内存中的大小,段的对齐标志,如上图所示。
一个可执行文件至少要有一个可加载类型的段,这种类型的段会被装载或映射进内存中,这个会在后面分析程序加载流程时会描述到。