release版本core推导与相应工具学习

本文介绍了ELF文件格式的基本概念,包括常用的命令工具、obj文件与可执行文件的段结构、进程内存映像等内容。此外,还讲解了符号表与调试信息的作用,以及如何利用gdb进行调试,并提供了coredump的生成与查看方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一般进行开发工作时,会有debug版本和release版本之分,debug版本方便调试,但与release版本相比,体积臃肿,运行速度慢。一般debug版本会带有符号表与调试信息,而release版本会把符号表和调试信息等strip掉。这时候,如果release版本出现core,而又不具备在debug版本复现的条件,就需要直接在release版本定位,由于没有符号表与调试信息,定位起来会比较麻烦。
先补充一下基础知识,涉及以下几个方面:
1.常用命令
2.obj文件与可执行文件段结构
3.进程内存映像
4.符号表与调试信息
5.gdb命令

一、常用命令

以下介绍的命令都是gnu binutils套件的一部分,手册地址链接。包含工具的概览如下:

命令用处
ar创建、修改和提取存档
nm列出对象文件中的符号
objcopy复制和翻译对象文件
objdump显示对象文件中的信息
ranlib生成索引以存档内容
size列出节大小和总大小
strings列出文件中的可打印字符串
strip丢弃符号
c++filt用于对编码C++符号进行反处理的筛选器,其实就是反编译
cxxfiltMS-DOS name for c++filt
addr2line将地址转换为文件和行
windmcWindows 消息资源的生成器
windres操作Windows 资源
dlltool创建生成和使用 DLL 所需的文件
readelf显示 ELF 格式文件的内容
elfedit更新 ELF 文件的 ELF 标头和属性

1.ar

ar - create, modify, and extract from archives

ar命令最常见的用法是将目标文件打包为静态链接库。

指令参数:

-d 删除备存文件中的成员文件。
-r 将文件插入备存文件中。
-t 显示备存文件中所包含的文件。
-x 自备存文件中取出成员文件。

选项参数:

c 建立备存文件。
v 程序执行时显示详细的信息。

使用实例:
1.创建静态库

$ ar -crv libmakefile_test.a cli.o lib.o resource.o
r - cli.o
r - lib.o
r - resource.o

2.使用静态库编译

$ gcc -o app_test_static  ./app/main.c -I ./include -L ./ -lmakefile_test -static
$ gcc -o app_test_dynamic ./app/main.c -I ./include -L ./ -lmakefile_test

-I选项指定头文件搜索路径
-L选项指定库文件搜索路径
-l选项指明需要链接库文件路径下的哪一个库
-static 在支持动态链接的系统,这个参数将覆盖-pie选项并且防止链接到共享库。在其他系统,这个参数无用。
查看编译的文件:

$ file app_test_static
app_test_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=2f7ba0b5feb0bee3acd3113608215c8b3fa25c43, for GNU/Linux 3.2.0, not stripped
$ file app_test_dynamic
app_test_dynamic: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b8f4aae218ee51b8559f737609d156d4dccd111c, for GNU/Linux 3.2.0, not stripped

关于静态库和动态库编译及使用,可查看这篇文章

2.nm

nm - list symbols from object files

常用参数:
-A: 在输出时每行前加上文件名;
-a: 输出所有符号,包含debugger-only symbols;
-B: BSD码显示,兼容MIPS nm;
-C: 将低级符号名解析为用户级名字,可以使得C++函数名更具可读性;
-D: 显示动态符号。该选项只对动态目标(如特定类型的共享库)有意义;
-f format/–format=format 使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd
-g: 只显示外部符号;
-l: 对于每个符号,使用debug信息找到文件名和行号;
-n: 按符号对应地址的顺序排序,而非按符号名字字符顺序排序;
-P: 按照POSIX2.0标准格式输出,等同于使用 -f posix;
-p: 按照目标文件中遇到的符号顺序显示,不排序;
-r: 反转排序;
-s: 当列出库成员符号时,包含索引。索引的内容:模块和其包含名字的映射;
-u: 只显示未定义符号;
–defined-only: 只显示定义了的符号。

$ cat app/main.c
#include<stdio.h>
#include"../include/cli.h"
#include"../include/lib.h"
#include"../include/resource.h"

int main()
{
    installcmd();
    openlib();
    initresource();
    return 0;
}
$ nm -n main.o
                 U _GLOBAL_OFFSET_TABLE_
                 U initresource
                 U installcmd
                 U openlib
0000000000000000 T main

第二列的符号含义

符号含义
A该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
B该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中。
C该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。
D该符号位于初始化数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。
G该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。
I该符号是对另一个符号的间接引用。
N该符号是一个debugging符号。
R该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。
S符号位于非初始化数据区,用于small object。
T该符号位于代码区text section。
U该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。
V该符号是一个weak object。
W该符号是没有被明确标记为weak object的弱符号类型。
-该符号是a.out格式文件中的stabs symbol。
?该符号类型没有定义。

3.objcopy

objcopy - copy and translate object files
常用参数:
–set-start val
设定新文件的起始地址为val,并不是所有格式的目标文件都支持设置起始地址。

–change-start incr
–adjust-start incr
通过增加incr量来调整起始地址,并不是所有格式的目标文件都支持设置起始地址。

–change-address incr
–adjust-vma incr
通过增加incr量调整所有sections的VMA(virtual memory address)和LMA(linear memory address),以及起始地址。

–only-keep-debug
对文件进行strip,移走所有不会被–strip-debug移走的section,并且保持调试相关的section原封不动。
–add-gnu-debuglink= 增加 .gnu_debuglink到文件中
-S 移除所有符号表
-g 移除调试符号和节

$objcopy --only-keep-debug algorithm algorithm.dbg
$ls
algorithm  algorithm.dbg
$strip -s algorithm
$objcopy --add-gnu-debuglink=$(pwd)/algorithm.dbg algorithm
$readelf -S algorithm
...
[28] .gnu_debuglink    PROGBITS         0000000000000000  0000303c
       0000000000000014  0000000000000000           0     0     4
...

4.objdump

objdump - display information from object files
常用参数:
-a :显示档案库的成员信息,类似ls -l将lib*.a的信息列出。
-C :将底层的符号名解码成用户级名字,除了去掉所开头的下划线之外,还使得C++函数名以可理解的方式显示出来。
-g :显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。有些其他的格式被readelf -w支持。
-e :类似-g选项,但是生成的信息是和ctags工具相兼容的格式。
-d :从objfile中反汇编那些特定指令机器码的section。可用于查看对应汇编的行号。
-D :与 -d 类似,但反汇编所有section.
–endian={big|little} :指定目标文件的大小端。这个项将影响反汇编出来的指令。在反汇编的文件没描述小端信息的时候用。例如S-records.
-f :显示objfile中每个文件的整体头部摘要信息。
-h :显示目标文件各个section的头部摘要信息。 如果想看.text段在文件中的偏移量(字节),可以查看File off字段。需要注意的是,偏移量可能是16进制的,但是里面显示没带0x
-i :显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
-I, --include=DIR 将指定文件夹添加到源文件搜索列表
-j name:仅仅显示指定名称为name的section的信息
-l:用文件名和行号标注相应的目标代码。
-m machine :指定反汇编目标文件时使用的架构,当待反汇编文件本身没描述架构信息的时候(比如S-records),这个选项很有用。可以用-i选项列出这里能够指定的架构.
-t :显示文件的符号表。类似于nm -s提供的信息
-T :显示文件的动态符号表,仅仅对动态目标文件意义,比如某些共享库。它显示的信息类似于 nm -D|–dynamic 显示的信息。
-r :显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
-R :显示文件的动态重定位入口,仅仅对于动态目标文件意义,比如某些共享库。
-s :显示指定section的完整内容。默认所有的非空section都会被显示。
-S :尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。 使用-Sld参数可以进行汇编行和c代码的对应。
–show-raw-insn :反汇编的时候,显示每条汇编指令对应的机器码,如不指定–prefix-addresses,这将是缺省选项。
–no-show-raw-insn :反汇编时,不显示汇编指令的机器码,如指定–prefix-addresses,这将是缺省选项。
–start-address=address :从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。
–stop-address=address :显示数据直到指定地址为止,该项影响-d、-r和-s选项的输出。
-x :显示所可用的头信息,包括符号表、重定位入口。-x 等价于-a -f -h -r -t 同时指定。
-z :一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。
@file 可以将选项集中到一个文件中,然后使用这个@file将选项载入。

$objdump -h out/usr/local/bin/algorithm

out/usr/local/bin/algorithm:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000000318  0000000000000318  00000318  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.gnu.property 00000030  0000000000000338  0000000000000338  00000338  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000000368  0000000000000368  00000368  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .note.ABI-tag 00000020  000000000000038c  000000000000038c  0000038c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .gnu.hash     00000028  00000000000003b0  00000000000003b0  000003b0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       00000108  00000000000003d8  00000000000003d8  000003d8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .dynstr       000000ca  00000000000004e0  00000000000004e0  000004e0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version  00000016  00000000000005aa  00000000000005aa  000005aa  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .gnu.version_r 00000030  00000000000005c0  00000000000005c0  000005c0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.dyn     000000d8  00000000000005f0  00000000000005f0  000005f0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .rela.plt     00000060  00000000000006c8  00000000000006c8  000006c8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .init         0000001b  0000000000001000  0000000000001000  00001000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .plt          00000050  0000000000001020  0000000000001020  00001020  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .plt.got      00000010  0000000000001070  0000000000001070  00001070  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .plt.sec      00000040  0000000000001080  0000000000001080  00001080  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 15 .text         00000151  00000000000010c0  00000000000010c0  000010c0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 16 .fini         0000000d  0000000000001214  0000000000001214  00001214  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 17 .rodata       0000002d  0000000000002000  0000000000002000  00002000  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 18 .eh_frame_hdr 0000003c  0000000000002030  0000000000002030  00002030  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 19 .eh_frame     000000bc  0000000000002070  0000000000002070  00002070  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 20 .init_array   00000008  0000000000003d80  0000000000003d80  00002d80  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .fini_array   00000008  0000000000003d88  0000000000003d88  00002d88  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .dynamic      00000210  0000000000003d90  0000000000003d90  00002d90  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .got          00000060  0000000000003fa0  0000000000003fa0  00002fa0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 24 .data         00000010  0000000000004000  0000000000004000  00003000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 25 .bss          00000010  0000000000004010  0000000000004010  00003010  2**4
                  ALLOC
 26 .comment      0000002b  0000000000000000  0000000000000000  00003010  2**0
                  CONTENTS, READONLY
 27 .debug_aranges 00000060  0000000000000000  0000000000000000  0000303b  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 28 .debug_info   000004a6  0000000000000000  0000000000000000  0000309b  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 29 .debug_abbrev 00000244  0000000000000000  0000000000000000  00003541  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 30 .debug_line   0000013a  0000000000000000  0000000000000000  00003785  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 31 .debug_str    000002a8  0000000000000000  0000000000000000  000038bf  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 32 .debug_line_str 0000011c  0000000000000000  0000000000000000  00003b67  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 33 .debug_loclists 00000028  0000000000000000  0000000000000000  00003c83  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 34 .debug_rnglists 00000017  0000000000000000  0000000000000000  00003cab  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
$objdump -a out/usr/local/lib/libparallelmenu.a
In archive out/usr/local/lib/libparallelmenu.a:

parallelmenu.o:     file format elf64-x86-64
rw-r--r-- 0/0   7216 Jan  1 08:00 1970 parallelmenu.o


parallelmenu_api.o:     file format elf64-x86-64
rw-r--r-- 0/0  25872 Jan  1 08:00 1970 parallelmenu_api.o

5.strip

strip - discard symbols and other data from object files
常用参数:
-s:去除所有符号表
-g:只去除debug符号表
-o:将strip的内容存储到另外的指定文件

$ strip a.out -o a_strip.out
$ ls
a.out  a.out.dbg  a_strip.out  test.c

如果使用了-g,那么调试时还是能看到函数名的,只不过可能看不到行号了。
实例:在使用-g前有38个节,debug就有8个

$readelf -S algorithm|grep debug
  [28] .debug_aranges    PROGBITS         0000000000000000  0000303b
  [29] .debug_info       PROGBITS         0000000000000000  0000309b
  [30] .debug_abbrev     PROGBITS         0000000000000000  00003541
  [31] .debug_line       PROGBITS         0000000000000000  00003785
  [32] .debug_str        PROGBITS         0000000000000000  000038bf
  [33] .debug_line_str   PROGBITS         0000000000000000  00003b67
  [34] .debug_loclists   PROGBITS         0000000000000000  00003c83
  [35] .debug_rnglists   PROGBITS         0000000000000000  00003cab
$readelf -p .strtab algorithm

String dump of section '.strtab':
  [     1]  Scrt1.o
  [     9]  __abi_tag
  [    13]  main.c
  [    1a]  crtstuff.c
  [    25]  deregister_tm_clones
  [    3a]  __do_global_dtors_aux
  [    50]  completed.0
  [    5c]  __do_global_dtors_aux_fini_array_entry
  [    83]  frame_dummy
  [    8f]  __frame_dummy_init_array_entry
  [    ae]  interface.c
  [    ba]  __FRAME_END__
  [    c8]  _DYNAMIC
  [    d1]  __GNU_EH_FRAME_HDR
  [    e4]  _GLOBAL_OFFSET_TABLE_
  [    fa]  __libc_start_main@GLIBC_2.34
  [   117]  _ITM_deregisterTMCloneTable
  [   133]  puts@GLIBC_2.2.5
  [   144]  stdin@GLIBC_2.2.5
  [   156]  _edata
  [   15d]  _fini
  [   163]  interface
  [   16d]  __data_start
  [   17a]  __gmon_start__
  [   189]  __dso_handle
  [   196]  _IO_stdin_used
  [   1a5]  _end
  [   1aa]  __bss_start
  [   1b6]  main
  [   1bb]  BST
  [   1bf]  __TMC_END__
  [   1cb]  parallelmenu
  [   1d8]  _ITM_registerTMCloneTable
  [   1f2]  __cxa_finalize@GLIBC_2.2.5
  [   20d]  _init
  [   213]  getc@GLIBC_2.2.5
$readelf -p .dynstr algorithm

String dump of section '.dynstr':
  [     1]  _ITM_deregisterTMCloneTable
  [    1d]  __gmon_start__
  [    2c]  _ITM_registerTMCloneTable
  [    46]  parallelmenu
  [    53]  BST
  [    57]  __cxa_finalize
  [    66]  __libc_start_main
  [    78]  getc
  [    7d]  puts
  [    82]  stdin
  [    88]  libparallelmenu.so.0
  [    9d]  libbst.so.0
  [    a9]  libc.so.6
  [    b3]  GLIBC_2.2.5
  [    bf]  GLIBC_2.34
$ readelf -s algorithm

Symbol table '.dynsym' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _[...]@GLIBC_2.34 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (3)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND BST
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND parallelmenu
     7: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getc@GLIBC_2.2.5 (3)
     9: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [...]@GLIBC_2.2.5 (3)
    10: 0000000000004010     8 OBJECT  GLOBAL DEFAULT   26 stdin@GLIBC_2.2.5 (3)

Symbol table '.symtab' contains 42 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS Scrt1.o
     2: 000000000000038c    32 OBJECT  LOCAL  DEFAULT    4 __abi_tag
     3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
     4: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
     5: 0000000000001110     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones
     6: 0000000000001140     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones
     7: 0000000000001180     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux
     8: 0000000000004018     1 OBJECT  LOCAL  DEFAULT   26 completed.0
     9: 0000000000003d88     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtor[...]
    10: 00000000000011c0     0 FUNC    LOCAL  DEFAULT   16 frame_dummy
    11: 0000000000003d80     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_in[...]
    12: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS interface.c
    13: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    14: 0000000000002128     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__
    15: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    16: 0000000000003d90     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC
    17: 0000000000002030     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR
    18: 0000000000003fa0     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_mai[...]
    20: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
    21: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5
    23: 0000000000004010     8 OBJECT  GLOBAL DEFAULT   26 stdin@GLIBC_2.2.5
    24: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    25: 0000000000001214     0 FUNC    GLOBAL HIDDEN    17 _fini
    26: 00000000000011d0    65 FUNC    GLOBAL DEFAULT   16 interface
    27: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    28: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    29: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    30: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used
    31: 0000000000004020     0 NOTYPE  GLOBAL DEFAULT   26 _end
    32: 00000000000010e0    38 FUNC    GLOBAL DEFAULT   16 _start
    33: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    34: 00000000000010c0    22 FUNC    GLOBAL DEFAULT   16 main
    35: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND BST
    36: 0000000000004010     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
    37: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND parallelmenu
    38: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
    39: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@G[...]
    40: 0000000000001000     0 FUNC    GLOBAL HIDDEN    12 _init
    41: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getc@GLIBC_2.2.5
$strip -g algorithm
$file algorithm
algorithm: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9b9abe6929571743b9a7f78b269953a53224a6b5, for GNU/Linux 3.2.0, not stripped

此时仍显示未被strip,但此时就只有30个节了。而且使用gdb已经无法看到行号了。
再次使用-s,则只剩下28个节

$strip -s algorithm
$file algorithm
algorithm: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9b9abe6929571743b9a7f78b269953a53224a6b5, for GNU/Linux 3.2.0, stripped

.symtab和 .strtab已经被去除, .shstrtab还在(专门存储Section名字的Section(Section Header String Table Section,简写为shstrtab)),.dynsym和.dynstr也都还在。因此大多数函数都不能断了,但是外部函数还能断,因为.dynsym还存在。

6.addr2line

addr2line - convert addresses into file names and line numbers
常用参数:
-a:在函数名、文件名和行号信息之前,以十六进制形式显示地址。
-C:将低级别的符号名解码为用户级别的名字。
-e:指定需要转换地址的可执行文件名,默认文件是a.out。
-f:在显示文件名、行号信息的同时显示函数名。
-s:仅显示每个文件名(the base of each file name)去除目录名。
-i:如果需要转换的地址是一个内联函数,则还将打印返回第一个非内联函数的信息。
-j:读取指定section的偏移而不是绝对地址。
-p:使打印更加人性化:每个地址(location)的信息都打印在一行上。
-r:启用或禁用递归量限制。
编译进程如下,执行后段错误:

int main()
{
	int *p = NULL;
	*p=0;
	printf("hello world!\n");
}

dmesg信息如下:

[ 2022.242578] a.out[6050]: segfault at 0 ip 0000558c30b8f161 sp 00007ffd40f0e6f0 error 6 in a.out[558c30b8f000+1000]
[ 2022.242606] Code: 00 c3 0f 1f 80 00 00 00 00 f3 0f 1e fa e9 77 ff ff ff f3 0f 1e fa 55 48 89 e5 48 83 ec 10 48 c7 45 f8 00 00 00 00 48 8b 45 f8 <c7> 00 00 00 00 00 48 8d 3d 96 0e 00 00 e8 dd fe ff ff b8 00 00 00
[ 2022.242619] potentially unexpected fatal signal 11.

0000558c30b8f161 - 558c30b8f000 = 0x161
需要注意有的系统可以直接使用报错地址查到,有的系统需要减去基地址

7.readelf

-a 显示全部信息,等价于 -h -l -S -s -r -d -V -A -I.
-h 显示elf文件开始的文件头信息.
-l - 显示程序头(段头)信息(如果有的话)。
-S 显示节头信息(如果有的话)。 如果想看.text段在文件中的偏移量(字节),可以查看Offset字段。需要注意的是,偏移量可能是16进制的,但是里面显示没带0x
-g 显示节组信息(如果有的话)。
-t 显示节的详细信息(-S的)。
-s 显示符号表段中的项(如果有的话),使用-Ws选项来查看符号表。
-e 显示全部头信息,等价于: -h -l -S -n --notes 显示note段(内核注释)的信息。
-r 显示可重定位段的信息。
-u 显示unwind段信息。当前只支持IA64 ELF的unwind段信息。
-d 显示动态段的信息。 .dynamic段
-V 显示版本段的信息。
-A 显示CPU构架信息。
-D 使用动态段中的符号表显示符号,而不是使用符号段。
-x 以16进制方式显示指定段内内容。number指定段表中段的索引,或字符串指定文件中的段名。
-p dump出指定节的内容,看指定节的内容就用他
-w[liaprmfFsoR] or --debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges] 显示调试段中指定的内容。
-I 显示符号的时候,显示bucket list长度的柱状图。
-W 宽行输出。
@file 可以将选项集中到一个文件中,然后使用这个@file选项载入。

二、obj文件与可执行文件段结构

linux下的obj文件和可执行文件都是elf格式,其结构如下:

ELF头部(ELF Header):ELF头部是ELF文件的第一个部分,包含了文件类型、机器类型、入口地址、程序头表偏移量、节头表偏移量等信息。

程序头表(Program Header Table):程序头表描述了可执行文件或共享库的段信息,包括段类型、段的虚拟地址、文件偏移量、段大小等信息。

节头表(Section Header Table):节头表描述了ELF文件中的所有节,包括代码段、数据段、符号表、重定位表等。每个节头表项包含了节的名称、类型、大小、偏移量等信息。

节区(Section):ELF文件的数据被组织成许多节,每个节都有一个唯一的名称和类型。常见的节区包括.text(代码段)、.data(数据段)、.bss(未初始化的数据段)等。

符号表(Symbol Table):符号表包含了ELF文件中的所有符号信息,包括函数名、变量名、常量等。每个符号表项包含了符号的名称、类型、值、大小等信息。

字符串表(String Table):字符串表包含了ELF文件中使用的所有字符串,包括节名、符号名、库名等。

重定位表(Relocation Table):重定位表包含了ELF文件中的所有重定位信息,用于修正代码和数据的地址。每个重定位表项包含了需要修正的地址、修正方式等信息。

三、进程内存映像

调试core时加载带调试信息so时需要加地址的原因:

参见《深入理解计算机系统笔记–备忘》:代码段与数据段之间有空隙(为了对齐要求),而栈,共享模块,堆由于安全性还会使用地址空间布局随机化,每次程序运行时,他们的地址都会改变。

core中info proc mappings得到地址为当时动态库加载的地址,而add-symbol-file需要指定文件的text段地址,因此我们的目标是调试时加载的地址就是当时动态库.text段的地址,debug信息也要加载在这。

四、符号表与调试信息

符号表包含.dynsym节和.symtab节
.dynsym保存了引用来自外部文件符号的全局符号。如printf库函数。.dynsym保存的符号是.symtab所保存符合的子集,.symtab中还保存了可执行文件的本地符号。如全局变量,代码中定义的本地函数等。
.dynsym对于动态链接可执行文件的执行是必需的,而.symtab只是用来进行调试和链接的。

Symbol table '.dynsym' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _[...]@GLIBC_2.34 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (3)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [...]@GLIBC_2.2.5 (3)

Symbol table '.symtab' contains 36 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS Scrt1.o
     2: 000000000000038c    32 OBJECT  LOCAL  DEFAULT    4 __abi_tag
     3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
     4: 0000000000001090     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones
     5: 00000000000010c0     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones
     6: 0000000000001100     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux
     7: 0000000000004010     1 OBJECT  LOCAL  DEFAULT   26 completed.0
     8: 0000000000003dc0     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtor[...]
     9: 0000000000001140     0 FUNC    LOCAL  DEFAULT   16 frame_dummy
    10: 0000000000003db8     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_in[...]
    11: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
    12: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    13: 00000000000020f0     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__
    14: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    15: 0000000000003dc8     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC
    16: 0000000000002014     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR
    17: 0000000000003fb8     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_mai[...]
    19: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
    20: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5
    22: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    23: 0000000000001168     0 FUNC    GLOBAL HIDDEN    17 _fini
    24: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    25: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    26: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    27: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used
    28: 0000000000004018     0 NOTYPE  GLOBAL DEFAULT   26 _end
    29: 0000000000001060    38 FUNC    GLOBAL DEFAULT   16 _start
    30: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    31: 0000000000001149    30 FUNC    GLOBAL DEFAULT   16 main
    32: 0000000000004010     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
    33: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
    34: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@G[...]
    35: 0000000000001000     0 FUNC    GLOBAL HIDDEN    12 _init

Linux进程的调试信息包含在ELF文件中的多个段中,这些段通常以.debug开头,例如:

.debug_info:包含了程序的调试信息,例如类型定义、变量定义、函数定义等。
.debug_line:包含了程序的源代码行号信息,用于在调试器中显示源代码。
.debug_str:包含了程序中使用的所有字符串,例如变量名、函数名等。
.debug_abbrev:包含了调试信息的缩写表,用于压缩调试信息。
.debug_loc:包含了程序中局部变量的地址信息,用于在调试器中显示局部变量的值。
.debug_ranges:包含了程序中全局变量和静态变量的地址范围信息,用于在调试器中显示变量的值。

因此当符号表全部被去除,nm -a无内容时,还可以查看.dynsym来确认库对外提供的外部接口,可以使用objdump的-T选项:

-T, --dynamic-syms Display the contents of the dynamic symbol table;

或者可以使用readelf的-s选项:

-s --syms Display the symbol table

关于backtrace_symbols符号解析,后续再研究

-g 选项:
-g 选项告诉编译器生成调试信息,这些信息用于在调试工具(如 gdb)中调试程序。它将提供源代码的位置映射到生成的机器代码,使得调试时可以使用变量名、函数名、行号等。
使用此选项编译的程序通常会包含更详细的调试信息,便于调试,但它并不影响调用栈信息的符号解析。
-rdynamic 选项:
-rdynamic 选项则是用于把程序中的所有符号(包括静态和动态的)添加到可执行文件的符号表中。这对于在运行时获得符号信息(如通过 backtrace_symbols)是必须的。 使用 -rdynamic
后,程序在运行时能更方便地访问函数名和其他符号信息,与调用栈跟踪等功能兼容。

五、GLibc版本查看

有时排查问题需要明确c库的版本,方法列举如下:

ldd --version
strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC
getconf GNU_LIBC_VERSION
/lib/x86_64-linux-gnu/libc.so.6 #直接执行

六、gdb相关命令

1.add-symbol-file

(gdb) help add-symbol-file
从FILE中加载符号,并假定FILE已经被动态加载。
用法:add-symbol-file FILE [-readnow | -readnever] [-o OFF] [ADDR] [-s SECT-NAME SECT-ADDR]…
ADDR是文件.text的起始地址。
每个-s,参数指定了一个段名和段地址,如果数据段和bss段与文本不连续,则应指定该参数。
SECT-NAME 是一个被加载在 SECT-ADDR的段的名字.
OFF是一个可选的偏移量,它被添加到默认加载地址中未指定其他地址的所有节的。
(OFF is an optional offset which is added to the default load addresses of all sections for which no other address was specified.)
'-readnow’选项将导致GDB立即读取整个符号文件。这可能使命令执行变慢,但可能使未来的操作更快。
'-readnever’选项将阻止GDB读取符号文件的符号调试信息。

2.info proc mappings

List memory regions mapped by the specified process.
列出特定进程的内存区域

3. info sharedlibrary

Status of loaded shared object libraries.
已加载共享库的状态

4.info proc mappings和info sharedlibrary的差异

wsl@My-win:~/project/algorithm$ objdump -h /usr/local/lib/libparallelmenu.so.0.0.0

/usr/local/lib/libparallelmenu.so.0.0.0:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .note.gnu.property 00000020  00000000000002a8  00000000000002a8  000002a8  2**3
...
...
 13 .text         00000928  00000000000012c0  00000000000012c0  000012c0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE

通过上述命令可以看到.text段相对于动态库的偏移是0x12c0.
如果直接运行进程,然后attach,查看命令:

(gdb) info proc mappings
process 9775
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x561bf6f6f000     0x561bf6f70000     0x1000        0x0 /usr/local/bin/algorithm
      ...
      ...
      0x561bf6f73000     0x561bf6f74000     0x1000     0x3000 /usr/local/bin/algorithm
      0x561bf7300000     0x561bf7321000    0x21000        0x0 [heap]
      0x7fe23e052000     0x7fe23e055000     0x3000        0x0
      0x7fe23e055000     0x7fe23e07d000    0x28000        0x0 /usr/lib/x86_64-linux-gnu/libc.so.6
      ...
      ...
      0x7fe23e26e000     0x7fe23e270000     0x2000   0x218000 /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7fe23e270000     0x7fe23e27d000     0xd000        0x0
      0x7fe23e27d000     0x7fe23e27e000     0x1000        0x0 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7fe23e27e000     0x7fe23e27f000     0x1000     0x1000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7fe23e27f000     0x7fe23e280000     0x1000     0x2000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7fe23e280000     0x7fe23e281000     0x1000     0x2000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7fe23e281000     0x7fe23e282000     0x1000     0x3000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7fe23e287000     0x7fe23e289000     0x2000        0x0
      0x7fe23e289000     0x7fe23e28b000     0x2000        0x0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      ...
      ...
      0x7fe23e2c3000     0x7fe23e2c5000     0x2000    0x39000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffd49381000     0x7ffd493a2000    0x21000        0x0 [stack]
      0x7ffd493d9000     0x7ffd493dd000     0x4000        0x0 [vvar]
      0x7ffd493dd000     0x7ffd493df000     0x2000        0x0 [vdso]
(gdb) info sharedlibrary
From                To                  Syms Read   Shared Object Library
0x00007fe23e27e2c0  0x00007fe23e27ebe8  Yes         /usr/local/lib/libparallelmenu.so.0
0x00007fe23e07d700  0x00007fe23e20fabd  Yes (*)     /lib/x86_64-linux-gnu/libc.so.6
0x00007fe23e28b090  0x00007fe23e2b4335  Yes (*)     /lib64/ld-linux-x86-64.so.2
(*): Shared library is missing debugging information.

如果从gdb启动,显示如下:

(gdb) info proc mappings
process 9789
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /usr/local/bin/algorithm
      ...
      ...
      0x555555558000     0x555555559000     0x1000     0x3000 /usr/local/bin/algorithm
      0x7ffff7d86000     0x7ffff7d89000     0x3000        0x0
      0x7ffff7d89000     0x7ffff7db1000    0x28000        0x0 /usr/lib/x86_64-linux-gnu/libc.so.6
      ...
      ...
      0x7ffff7fa2000     0x7ffff7fa4000     0x2000   0x218000 /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7fa4000     0x7ffff7fb1000     0xd000        0x0
      0x7ffff7fb1000     0x7ffff7fb2000     0x1000        0x0 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7ffff7fb2000     0x7ffff7fb3000     0x1000     0x1000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7ffff7fb3000     0x7ffff7fb4000     0x1000     0x2000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7ffff7fb4000     0x7ffff7fb5000     0x1000     0x2000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7ffff7fb5000     0x7ffff7fb6000     0x1000     0x3000 /usr/local/lib/libparallelmenu.so.0.0.0
      0x7ffff7fbb000     0x7ffff7fbd000     0x2000        0x0
      0x7ffff7fbd000     0x7ffff7fc1000     0x4000        0x0 [vvar]
      0x7ffff7fc1000     0x7ffff7fc3000     0x2000        0x0 [vdso]
      0x7ffff7fc3000     0x7ffff7fc5000     0x2000        0x0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      ...
      ...
      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x39000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
(gdb) info sharedlibrary
From                To                  Syms Read   Shared Object Library
0x00007ffff7fc5090  0x00007ffff7fee335  Yes (*)     /lib64/ld-linux-x86-64.so.2
0x00007ffff7fb22c0  0x00007ffff7fb2be8  Yes         /usr/local/lib/libparallelmenu.so.0
0x00007ffff7db1700  0x00007ffff7f43abd  Yes (*)     /lib/x86_64-linux-gnu/libc.so.6
(*): Shared library is missing debugging information.

总结:
info proc mappings查看的是动态库运行时加载的地址,如果是直接启动的,这个地址是随机的,如果是gdb启动的,这个地址是固定的。
info sharedlibrary 命令查看的是动态库代码段的起始地址。

4.x与disassemble的异同

(gdb) help x
检查内存:x/FMT ADDRESS。
ADDRESS是要检查的内存地址的表达式。
FMT是一个重复计数,后面跟着一个格式字母和一个大小字母。
格式字母有o(八进制),x(十六进制),d(十进制),u(无符号十进制),
T(二进制),f(浮点),a(地址),i(指令),c(char), s(字符串)
z(十六进制,0在左边填充)
大小字母是b(字节),h(半字),w(字),g(巨型,8字节)。
打印指定大小的指定数量的对象
根据格式。如果指定的是负数,则内存为从地址往后查。
字母的格式和大小的默认值是以前使用的。
默认计数为1。默认地址在最后打印的内容后面用这个命令或者"print"。
(gdb) help disassemble
反汇编内存的指定部分。
默认是所选帧的pc周围的函数。

如果使用/m修饰符,那么将包括源代码行(如果可用)。
这种视图是“以源代码为中心”的:输出按源行顺序排列,而不是优化的代码。只有主源文件
被显示,而不是别的,例如,任何内联函数。这个修饰符在实践中没有被证明是有用的,因此不建议使用

使用/s修饰符,那么将包括源代码行(如果可用)。这与/m在两个重要方面不同:
-输出仍然是PC地址顺序
-显示所有相关源文件的文件名和内容。

使用/r修饰符,将包括十六进制的原始指令。

如果只有一个参数,围绕该地址的函数将被转储。两个参数(以逗号分隔)作为要转储的内存范围,以“start,end”或“start,+length”的形式。

注意,地址被解释为一个表达式,而不是一个位置,就像"break"命令一样。
因此,例如,如果你想在文件foo.c中反汇编函数bar,你必须输入“disassemble ‘foo.c’::bar”而不是“disassemble 'foo.c:bar”。

七、coredump

coredump是一个内存映像,可以是主动生成,也可以是崩溃自动生成。

查看coredump开启情况

查看coredump开启情况:

$ ulimit -c 
0

coredump功能在linux是默认关闭的。

打开 coredump 功能

(1)临时打开 coredump 功能
ulimit -c unlimited 命令:生成 coredump 文件,文件大小不受限制。 或ulimit -c 1024 命令: 生成固定大小的 coredump 文件,单位为 blocks(KB)。
注意:这种修改为临时限制,只存活于当前 shell 会话,仅影响当前 shell。

wsl@My-win:~$ ulimit -c unlimited
wsl@My-win:~$ ulimit -c
unlimited

(2)永久打开 coredump 功能
更改 /etc/security/limits.conf 文件中的内容。
去掉 soft core 0 一行前面的注释 ,同时,将 0 改为 unlimited

#/etc/security/limits.conf
...
#

#*               soft    core            0
#root            hard    core            100000
#*               hard    rss             10000

设置coredump路径

默认生成路径:输入可执行文件运行命令的同一路径下
默认生成名字:默认命名为core。新的core文件会覆盖旧的core文件
以下命令分别添加了进程号和日期作为core文件名字

echo 1 > /proc/sys/kernel/core_uses_pid
echo "/corefile/core-%e-%p-%t" > core_pattern

测试core文件生成情况

kill -s SIGSEGV $$

离线查看

离线查看core是很常见的,因为出问题的设备很可能资源有限。把源文件直接放到core文件同路径就可以看函数以及行号了。离线调试core文件需要对应的交付件与符号表,交付件可以是出问题的设备拷贝出来的,也可以是重编的带符号表的同参数编译的交付件,而符号表可以使用objcopy单独剥离的。

八、符号表剥离与附加

查看.text段在文件中的偏移量(字节)
1.objdump -h 的File off字段

 Idx Name          Size      VMA               LMA               File off  Algn
 12 .text         00145e2b  0000000000022320  0000000000022320  00022320  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE

2.readelf -S 的Offset字段

  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align				  
  [13] .text             PROGBITS         0000000000022320  00022320
       0000000000145e2b  0000000000000000  AX       0     0     16

需要注意的是,偏移量可能是16进制的,但是里面显示没带0x

在使用add-symbol-file时,需要指定.text段的地址,即info proc mappings对应的起始地址+.text段偏移量,需要注意的是,如果在看偏移量时发现地址和偏移不一致,可以直接使用地址,不需要再加.text段偏移了,具体原因未知,可能和x86地址随机化有关

同样的代码,在x86_64:

(gdb) add-symbol-file test 0x559129f91000+0x1060
add symbol table from file "test" at
        .text_addr = 0x559129f92060
(y or n) y
Reading symbols from test...done.
(gdb) bt
#0  0x0000559129f92171 in main () at test.c:13
#1  0x00007f113bf9409b in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x0000559129f9208a in ?? ()

在arm64:

(gdb) add-symbol-file test_arm64 0x000000004004f0
add symbol table from file "test_arm64" at
        .text_addr = 0x4004f0
(y or n) y
Reading symbols from test_arm64...
(gdb) bt
#0  0x0000000000400624 in main () at test.c:13

如果是动态库,可以直接 info sharedlibrary查看库的地址,然后直接使用add-sys mbol-file 加载

剥离符号表流程

$ aarch64-none-linux-gnu-objcopy  --only-keep-debug test_arm64 test_arm64.dbg
$ file test_arm64.dbg 
test_arm64.dbg: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter *empty*, for GNU/Linux 3.7.0, with debug_info, not stripped
$ aarch64-none-linux-gnu-readelf -S test_arm64.dbg 
There are 27 section headers, starting at offset 0x3b0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           NOBITS           0000000000400238  00000238
       000000000000001b  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .hash             NOBITS           0000000000400278  00000274
       000000000000002c  0000000000000004   A       5     0     8
  [ 4] .gnu.hash         NOBITS           00000000004002a8  00000274
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           NOBITS           00000000004002c8  00000274
       0000000000000090  0000000000000018   A       6     1     8
  [ 6] .dynstr           NOBITS           0000000000400358  00000274
       000000000000004b  0000000000000000   A       0     0     1
  [ 7] .gnu.version      NOBITS           00000000004003a4  00000274
       000000000000000c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    NOBITS           00000000004003b0  00000274
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         NOBITS           00000000004003d0  00000274
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         NOBITS           00000000004003e8  00000274
       0000000000000078  0000000000000018   A       5    22     8
  [11] .init             NOBITS           0000000000400460  00000274
       0000000000000014  0000000000000000  AX       0     0     4
  [12] .plt              NOBITS           0000000000400480  00000274
       0000000000000070  0000000000000010  AX       0     0     16
  [13] .text             NOBITS           00000000004004f0  00000274
       00000000000001cc  0000000000000000  AX       0     0     8

gdb中:

(gdb) add-symbol-file test_arm64.dbg 0x000000004004f0
add symbol table from file "test_arm64.dbg" at
        .text_addr = 0x4004f0
(y or n) y
Reading symbols from test_arm64.dbg...
(gdb) bt
#0  0x0000000000400624 in main () at test.c:13

需要注意 test_arm64.dbg与test_arm64中的.text偏移是一样的

使用效果

(gdb) bt
#0  0x0000ffffae295c84 in epoll_pwait () from target:/lib64/libc.so.6
#1  0x0000000000e169cc in thread_fetch3 ()
#2  0x00000000005286c0 in cml_start ()
#3  0x000000000050aee0 in main ()
(gdb) add-symbol-file cmld.dbg 0x0000000050df10
add symbol table from file "cmld.dbg" at
        .text_addr = 0x50df10
(y or n) y
Reading symbols from cmld.dbg...
(gdb) bt
#0  0x0000ffffae295c84 in epoll_pwait () from target:/lib64/libc.so.6
#1  0x0000000000e169cc in nsm_encode_cfm_status_msg (pnt=0xfffffffffffffffc, size=0xfffffc30af00, msg=0x42) at nsm_message.c:17442
#2  0x00000000005286c0 in cml_check_if_default_value_set_in_db (header=0x1, tranObj=<optimized out>) at cml_main.c:17847
#3  0x000000000050aee0 in main ()

远程gdb使用效果

(gdb) target extended-remote 172.14.49.68:6000
(gdb) bt
#0  0x0000ffffae295c84 in epoll_pwait () from target:/lib64/libc.so.6
#1  0x0000000000e169cc in thread_fetch3 ()
#2  0x00000000005286c0 in cml_start ()
#3  0x000000000050aee0 in main ()
(gdb) add-symbol-file cmld.dbg 0x0000000050df10
add symbol table from file "cmld.dbg" at
        .text_addr = 0x50df10
(y or n) y
Reading symbols from cmld.dbg...
(gdb) bt
#0  0x0000ffffae295c84 in epoll_pwait () from target:/lib64/libc.so.6
#1  0x0000000000e169cc in nsm_encode_cfm_status_msg (pnt=0xfffffffffffffffc, size=0xfffffc30af00, msg=0x42) at nsm_message.c:17442
#2  0x00000000005286c0 in cml_check_if_default_value_set_in_db (header=0x1, tranObj=<optimized out>) at cml_main.c:17847
#3  0x000000000050aee0 in main ()
(gdb) 

参考文章:
Linux环境Release版本的符号表剥离及调试方法
Why does the same shared library show up multiple times when using info proc mappings in gdb?
为什么 ‘ldd’ 和 ‘(gdb) info sharedlibrary’ 显示不同的动态库基地址?
Thread: addr2line doesn’t work - returns ??:0
如何在没有core文件的情况下用dmesg+addr2line定位段错误
Linux下离线调试之coredump文件介绍
计算机那些事(4)——ELF文件结构
strip,eu-strip 及其符号表,gdb调试strip过的程序
Linux调试工具 | Addr2line
addr2line 输出为?:0可能原因

<think>嗯,用户想快速学习3GPP协议,这个任务看起来有点挑战性。首先,我得想想3GPP协议的范围有多大。3GPP涵盖了很多技术标准,从无线接入到核心网,还有各种服务架构。用户可能是通信行业的工程师或者学生,需要掌握这些协议来工作或研究。他们可能需要一个结构化的学习路径,而不是零散的信息。 接下来,用户提到的“快速学习”意味着效率很重要。可能需要强调如何找到重点,避免陷入过多的细节。然后,系统级指令要求用中文,格式要正确,特别是数学表达式的部分,不过3GPP协议更多是技术文档,可能数学公式不多,但如果有的话要确保正确使用LaTeX。 用户可能没有说出来的需求包括如何获取资料,如何理解协议的结构,以及实际应用的例子。比如,协议文档通常很冗长,直接阅读可能效率低下,所以需要指导如何有效查阅,比如先看概述部分,再深入细节。 还需要考虑学习资源,比如官方网站、书籍、在线课程或者论坛。这些资源能帮助用户更高效地学习。另外,实践环节很重要,比如通过仿真工具或实际项目来应用协议知识,加深理解。 可能的分步建议包括:确定学习目标,比如专注5G还是4G;掌握协议结构,比如文档编号规则;结合实践,如使用测试工具;参考权威资料和社区讨论。同时,强调持续学习的重要性,因为3GPP标准会不断更新。 最后,确保回答结构清晰,分点列出,每个步骤有具体的行动建议,帮助用户一步步构建知识体系。避免使用专业术语过多,但必要时解释清楚。可能还需要提醒用户遇到问题时的解决方法,比如加入技术社区或请教专家。</think>要高效学习3GPP协议,建议按照以下结构化步骤进行: ### 一、明确学习目标 1. **技术领域聚焦** - 3GPP协议涵盖无线接入网(RAN)、核心网(Core)、业务架构(SA)等,需明确主攻方向(如5G NR物理层、核心网信令流程等)。 2. **版本选择** - 优先学习最新稳定版本(例如Release 18),但需结合项目需求(如现网部署多为Release 15/16)。 ### 二、掌握协议文档结构 1. **文档编号规则** - 协议文档编号格式:`系列号.xxx` $$ \text{示例:38.331 → 5G NR RRC协议,38系列为NR专属} $$ - 关键系列号: - 21系列:需求架构 - 23系列:核心网流程 - 36系列:LTE - 38系列:NR 2. **文档内容分层** - Stage 1(服务需求)→ Stage 2(系统设计)→ Stage 3(代码级实现) ### 三、高效学习方法 1. **“三段式”阅读法** - **Step 1**:精读协议文档的**第1章范围(Scope)和第4章总体描述(Overview)**,建立框架认知。 - **Step 2**:通过流程图/时序图(如TS 23.502中的业务流程)理解交互逻辑。 - **Step 3**:结合伪代码(如TS 38.214的调度算法推导实现细节。 2. **工具辅助** - 使用协议阅读工具(如3GPP官方的SpecViewer)进行交叉引用跳转: $$ \text{快速定位相关协议:} \text{TS 38.300} \xrightarrow{\text{引用}} \text{TS 38.331} $$ ### 四、实践验证 1. **仿真环境搭建** - 使用开源工具(如ns-3、srsRAN)复现协议流程,例如: ```python # 示例:5G NR调度算法仿真片段 def time_domain_scheduler(ue_list): for slot in frame: alloc_ue = max(ue_list, key=lambda ue: ue.cqi) allocate_rb(alloc_ue, slot) ``` 2. **信令跟踪分析** - 使用Wireshark解析NAS/RRC消息(过滤器示例): ```bash wireshark -r trace.pcap -Y "nas-5gs || rrc.nr" ``` ### 五、资源推荐 1. **官方渠道** - [3GPP官网](https://www.3gpp.org/)的Specification Portal提供最新文档下载 2. **书籍参考** - 《5G NR: The Next Generation Wireless Access Technology》详细解读38系列协议 3. **社区交流** - 参3GPP delegate博客、Stack Overflow的`3gpp`标签讨论 ### 六、学习路线示例(以5G NSA组网为例) 1. 阶段1:理解双连接架构(TS 37.340) 2. 阶段2:掌握LTE-NR协作流程(TS 36.300/38.300) 3. 阶段3:分析X2/Xn接口信令(TS 38.423/TS 38.413) > **关键提示**:遇到协议矛盾时,优先参考最新版本和对应TR(技术报告)中的结论。例如MIMO层数映射规则在TS 38.214的Table 5.2.2-2可能存在跨版本差异,需结合RAN1会议纪要(R1-xxxxxx)确认。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值