跳转至

Linux 研发

1 环境变量

1.1 添加系统全局 C/C++ 头文件、库路径

系统默认 include 目录是 /usr/include 目录。默认的 lib 库加载路径是 /usr/local/bin 目录

1.2 CPATH

修改 CPATH 环境变量(在/etc/profile 或~/.bash_profile 里面),添加新的头文件包含目录,重新连接终端生效,gcc 编译器会在此目录下查找头文件。 - 示例:export CPATH=$CPATH:/opt/rocksdb-7.6/include - 查看头文件搜索目录:使用此方式可以查到刚刚添加的/opt/rocksdb-7.6/include 的目录

# 查看gcc预处理C时的的搜索目录:
[rocksdb@centos7 ~]$ echo | gcc -x c -v -E -
# 查看gcc预处理C++时的的搜索目录:
[rocksdb@centos7 ~]$ echo | gcc -x c++ -v -E -
# 查看clang预处理C++时的搜索目录:
[rocksdb@centos7 ~]$ echo | clang -x c++ -v -E -

1.3 LIBRARY_PATH

title: LIBRARY_PATH和LD_LIBRARY_PATH区别?
`LIBRARY_PATH` 用于程序编译时,`LD_LIBRARY_PATH`用于程序运行时。 

LIBRARY_PATH 是程序编译期间查找链接库时指定的查找路径。如果编译时报链接某个库失败,那么我们可以把缺少的库所在目录指定到 LIBRARY_PATH,重新编译即可。

1.4 LD_LIBRARY_PATH

LD_LIBRARY_PATH 是程序运行时链接动态库的时指定查找的路径。 修改 LD_LIBRARY_PATH 环境变量,添加新的库目录,重新连接终端生效。 - 示例:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/rocksdb-7.6/lib

1.5 LD_PRELOAD 影响程序运行时动态库的链接

  • 动态库加载顺序:LD_PRELOAD>LD_LIBRARY_PATH>/etc/ld.so.cache>/lib>/usr/lib
  • 使用实例:LD_PRELOAD=动态库路径 待执行程序路径和名,如 LD_PRELOAD=/home/user01/libxxx.so /home/user01/test,这时 test 程序会优先加载 /home/user01/libxxx.so 这个动态库

2 c++filt

  • 功能:翻译 c++ 或 java 符号到标准可读名称
  • 语法:c++filt [option] symbol
  • -p: 当处理的 symbol 时函数时,不打印函数参数
  • -_: 删除名称前的下划线。On some systems, both the C and C++ compilers put an underscore in front of every name. For example, the C name "foo" gets the low-level name "_foo". This option removes the initial underscore.
  • -n: 不删除名称前的下划线
  • 示例
$ c++filt _ZN2ns3fooIdbEEvT_T0_
void ns::foo<double, bool>(double, bool)
$ c++filt -_ _ZN2ns3fooIdbEEvT_T0_
_ZN2ns3fooIdbEEvT_T0_
$ c++filt -n _ZN2ns3fooIdbEEvT_T0_
void ns::foo<double, bool>(double, bool)
$ c++filt -p _ZN2ns3fooIdbEEvT_T0_
ns::foo<double, bool>

3 nm

nm 是一个用于查看二进制文件中符号(symbols)信息的工具。符号可以是函数、变量、常量等,在编译链接过程中由编译器和链接器生成。nm 工具允许您查看这些符号的地址、类型和其他相关信息。

  • 功能:查看 obj 文件符号信息,包括 lib,so,o
  • 语法形式:nm [option] filename
  • 参考资料

3.1 基本语法

基本语法

nm [options] <binary_file>

常用选项

  • -A:显示所有符号,包括隐藏的和弱符号。
  • -a:等同于 -A
  • -g:仅显示全局符号。
  • -l:显示源文件和行号信息(需要在编译时启用调试信息)。
  • -C:将 C++ 操作符符号转换为可读的形式。
  • -t:显示目标文件格式类型。
  • -u:显示未定义符号。
  • -D:显示动态符号表。
  • -r:按地址排序。
  • -S:显示符号大小。
  • --size-sort:按大小排序。
  • --defined-only:仅显示已定义的符号。

示例用法

nm -A /usr/bin/ls

可能的输出片段

0000000000403a20 T __libc_csu_fini
0000000000403a40 T __libc_csu_init
0000000000402d60 T main
                 U malloc@@GLIBC_2.2.5
                 U opendir@@GLIBC_2.2.5
                 U printf@@GLIBC_2.2.5

示例输出解释: 1. 第一列显示符号的虚拟地址; 2. 第 2 列显示的是符号类型; 3. 第 3 列显示的是符号名;

3.2 符号类型

  1. A:绝对符号(Absolute Symbol)
  2. 绝对符号的地址在链接时已经确定,不会受到重定位的影响。
  3. 通常是一些在代码中定义的常量。
  4. B:BSS 段数据(Uninitialized Data Segment)
  5. 表示位于 BSS 段中的未初始化的全局或静态变量。
  6. 这些变量在程序启动时会被初始化为零或空值。
  7. C:弱引用(Common Symbols)
  8. 表示具有弱链接的全局变量。
  9. 如果有多个弱引用符号具有相同的名称,则会选择其中的一个进行链接。
  10. D:数据段数据(Initialized Data Segment)
  11. 表示位于数据段中的已初始化的全局或静态变量。
  12. d:局部数据段数据(Local Initialized Data Segment)
  13. 类似于 D 类型,但是这些符号是局部的,仅在当前编译单元中可见。
  14. G:全局符号(Global Symbols)
  15. 表示全局可见的符号,可以在其他文件中使用。
  16. 在多个文件中定义相同名称的全局符号会导致链接错误。
  17. g:局部全局符号(Local Global Symbols)
  18. 类似于 G 类型,但是这些符号是局部的,仅在当前编译单元中可见。
  19. i:没有初始化的标识符(Indirect Symbols)
  20. 用于指示一个间接符号,通常在动态链接时使用。
  21. R:只读数据段数据(Read-Only Data Segment)
  22. 表示位于只读数据段中的数据,如常量字符串。
  23. r:局部只读数据段数据(Local Read-Only Data Segment)
    • 类似于 R 类型,但是这些符号是局部的,仅在当前编译单元中可见。
  24. S:符号大小(Symbol Size)
    • 用于显示符号的大小信息。
  25. T:文本段数据(Code Segment)
    • 表示代码段中的全局函数。
  26. t:局部文本段数据(Local Code Segment)
    • 类似于 T 类型,但是这些符号是局部的,仅在当前编译单元中可见。
  27. U:未定义符号(Undefined Symbols)
    • 表示在当前模块中引用但未定义的符号。
    • 需要在链接时解决这些未定义符号。
  28. w:弱符号(Weak Symbols)
    • 表示具有弱链接的全局函数或变量。
    • 如果有多个弱符号具有相同的名称,则会选择其中的一个进行链接。
  29. V:外部可见的符号(Hidden Symbols)
    • 表示在其他文件中定义的外部可见符号。
    • 仅在某些特定情况下可见。
  30. v:局部外部可见的符号(Local Hidden Symbols)
    • 类似于 V 类型,但是这些符号是局部的,仅在当前编译单元中可见。

3.3 查看导出符号 (包含函数参数信息)

nm -n -C xxx.so

3.4 查看.so 文件导出函数

nm -D xxx.so | awk '{if($2=="T"){print $3}}'

4 objdump

  • 功能:objdump 工具用来显示二进制文件的信息。

4.1 常用参数说明

  • -f 显示文件头信息
  • -D 反汇编所有 section (-d 反汇编特定 section)
  • -h 显示目标文件各个 section 的头部摘要信息
  • -x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。
  • -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
  • -r 显示文件的重定位入口。如果和 -d 或者 -D 一起使用,重定位部分以反汇编后的格式显示出来。
  • -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些共享库。
  • -S 尽可能反汇编出源代码,尤其当编译的时候指定了 -g 这种调试参数时,效果比较明显。隐含了 -d 参数。
  • -t 显示文件的符号表入口。类似于 nm -s 提供的信息

5 objcopy

  • 功能:通常用于复制、转换和修改二进制对象文件。它是 GNU Binutils 软件包的一部分,主要用于处理目标文件、可执行文件和共享库文件。objcopy 可以用于将一个目标文件的内容复制到另一个文件中,也可以用于转换文件格式、修改目标文件的各种属性等等。
  • 示例:将可执行文件调试信息分离,gdb 时手动加载调试文件
$ gcc -g test.c test
$ objcopy --only-keep-debug  test test.debug
$ strip test -o test.release
$ gdb ./test
(gdb) symbol-file get_ip.debug

--strip-all:删除目标文件中的所有符号信息和调试信息,生成一个精简的目标文件。

$ objcopy --strip-all get_ip get_ip.strip.all

--strip-debug:仅删除调试信息,保留符号信息。

bash $ objcopy --strip-debug get_ip get_ip.strip.debug

--add-gnu-debuglink: 指定链接符号文件

$ objcopy --add-gnu-debuglink=get_ip.strip.debug get_ip.release
$ objdump -s -j .gnu_debuglink get_ip.release

get_ip.release:     文件格式 elf64-x86-64

Contents of section .gnu_debuglink:
 0000 6765745f 69702e73 74726970 2e646562  get_ip.strip.deb
 0010 75670000 dd4a4e95                    ug...JN.    

6 readelf

  • 参考博客 ELF 文件有三种类型:
  • REL (Relocatable file) 可重定位的对象文件,由汇编器汇编生成的 .o 文件
  • EXEC (Executable file) 可执行的对象文件,如 window 上.exe 文件
  • DYN (Shared object file) 可被共享的对象文件,.so 文件
  • 示例
[root@centos7 perf_example]# readelf -h /lib64/libm.so.6
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x5350
  Start of program headers:          64 (bytes into file)
  Start of section headers:          1134704 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         7
  Size of section headers:           64 (bytes)
  Number of section headers:         35
  Section header string table index: 34

7 strings

  • 功能:打印文件里面的可打印字符。(可以打印普通文本文件,但一般用于二进制文件中)
  • 语法:string [option] file
    • -a:扫描整个文件,无论它包含哪些 section,或者这些 section 是否已加载或初始化。(默认选项)
    • -d:仅仅打印在文件中已加载或初始化的 section 的可打印字符
    • -n num:指定打印的字符序列最小长度(默认是 4byte)
    • -f:在输出的每行字符前打印所属的文件
    • -t radix:在每个字符串之前打印在文件中的偏移量
  • 示例
[root@centos7 ~]# strings -fd -t x a.out 
a.out:     238 /lib64/ld-linux-x86-64.so.2
a.out:     31f |k       C
a.out:     5c9 libstdc++.so.6
a.out:     5d8 __gmon_start__
a.out:     5e7 __cxa_demangle
a.out:     5f6 libm.so.6
a.out:     600 libgcc_s.so.1
a.out:     60e libc.so.6
a.out:     618 puts
a.out:     61d printf
a.out:     624 malloc
...中间省略...
a.out:     975 UH-h `
a.out:     9a5 UH-h `
a.out:     e2a []A\A]A^A_
a.out:     e63 %s:%s+%s
a.out:     e6d %s:%s()+%s
a.out:     e79 ----------done----------
a.out:     f57 ;*3$"

7.1 查看 GLIBC 版本

$ strings /usr/lib64/libstdc++.so.6|grep GLIBCXX
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14

8 strip

strip 命令在 Linux 中用于去除可执行文件和目标文件中的符号信息,以减小文件的大小。这对于发布或部署软件时可以有效地减少文件的体积。以下是 strip 命令的一些使用示例:

1. 基本用法:

strip <filename>

这将从指定的文件中删除符号信息,文件名可以是可执行文件或目标文件。

2. 删除符号信息并保存到不同文件:

strip -o <output_file> <input_file>

这将从输入文件中删除符号信息,并将结果保存到指定的输出文件。

3. 删除指定符号信息:

strip --strip-symbols=<symbol_list> <filename>

这将从文件中删除列表中指定的符号。例如,你可以指定 -o 选项来保留全局符号(global symbols)。

4. 仅删除调试符号:

strip --strip-debug <filename>

这将删除调试符号,但保留其他符号。

5. 仅删除全局符号:

strip --strip-unneeded <filename>

这将删除除全局符号以外的其他符号,可以用于减小文件大小。

6. 遍历目录并处理所有文件:

find /path/to/directory -type f -exec strip {} \;

这将在指定目录下递归地遍历所有文件,并对每个文件执行 strip 命令。

注意: 使用 strip 命令时,请务必小心。删除符号信息可能会影响调试和分析能力。最好的做法是在发布之前创建备份,并确保你知道自己在做什么。

9 调试信息和符号信息

9.1 调试信息(Debug Information)

调试信息是编译器在编译过程中生成的一种元数据,用于帮助调试器在调试过程中定位和理解源代码。调试信息通常包含以下内容:

  1. 源代码位置:调试信息记录了源代码文件名、行号和列号,使得调试器可以在源代码中定位到具体的代码行。
  2. 变量和数据结构:调试信息记录了变量的名称、类型、作用域和内存地址,使得调试器可以显示和操作变量的值。
  3. 函数和调用栈:调试信息记录了函数的名称、参数、返回类型和调用关系,使得调试器可以显示和操作调用栈。
  4. 编译单元:调试信息记录了编译单元的信息,如编译选项、预处理定义等。

调试信息通常以特定的格式存储在二进制文件中,如 DWARF(Debugging With Attributed Record Formats)格式。调试信息通常占用较大的空间,因此在发布版本中通常会被剥离以减小文件大小。

9.2 符号信息(Symbol Information)

符号信息是编译器在编译过程中生成的一种元数据,用于在运行时解析函数和变量的名称。符号信息通常包含以下内容:

  1. 函数符号:符号信息记录了函数的名称、地址和大小,使得链接器和调试器可以在运行时解析函数的地址。
  2. 变量符号:符号信息记录了全局变量和静态变量的名称、地址和大小,使得链接器和调试器可以在运行时解析变量的地址。
  3. 重定位信息:符号信息记录了需要重定位的地址和符号,使得链接器可以在链接过程中进行地址重定位。

符号信息通常以特定的格式存储在二进制文件中,如 ELF(Executable and Linkable Format)格式。符号信息在运行时解析函数和变量的地址,因此在发布版本中通常会被保留。

10 代码中打印函数调用栈信息

[[函数调用栈实现]]

11 gcore

  • 功能:生成 core dump 文件(可以使用 gdb 分析)
  • 语法:gcore [-a] [-o filename] pid
  • -a :dump 所有内存映射。会禁用 "use-coredump-filter" 并启用 "dump-excluded-mappings"。
  • -o filename :指定生成 core 文件名,如果不指定,默认是 core. pid。
  • 示例
$ gcore  26342
[New LWP 26924]
[New LWP 26879]
[New LWP 26877]
[New LWP 26876]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007fafaaa68b3b in do_futex_wait.constprop.1 () from /lib64/libpthread.so.0
warning: target file /proc/26342/cmdline contained unexpected null characters
Saved corefile core.26342
[Inferior 1 (process 26342) detached]
$ ll
-rw-rw-r--. 1 shuhw shuhw 2264032528 8月  10 13:54 1.core.26342

12 修改 core 文件配置

12.1 修改 core 文件生成位置

/proc/sys/kernel/core_pattern 用来配置 core dump 文件生成位置、文件名。 - 示例:echo "/tmp/core-%e-%p-%t" > /proc/sys/kernel/core_pattern, 其中 /tmp 是生成文件所在的文件夹(任意有权限的位置),core-%e-%p-%t 是生成 core 文件名的格式。(注意,使用 vi 编辑失败,只能使用 echo) - 文件名格式如下

%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名

12.2 修改 core 文件大小

  • 临时修改:(当前 shell 及子 shell 有效)使用 ulimit -c unlimited 使 core 文件大小无限制。
  • 修改 /etc/security/limits.conf 配置文件, 编辑如下
*       soft core      unlimited
*       hard core      unlimited

13 修改系统限制资源

/etc/security/limits.d下按照字母顺序排列的配置文件会覆盖 /etc/security/limits.conf中的 domain相同的的配置

limits.conf 文件的全路径是 /etc/security/limits.conf,是 conf-pamlimits 模块的配置文件。 每一行内容格式如下:

<domain>      <type>  <item>         <value>
  • <domain> :表示的是作用范围,可以是用户、用户组
  • user_name :用户名
  • @group_name :用户组名
  • * :所用用户
  • % :(比较复杂,不解释)
  • <type> :有软限制和硬限制区别,软限制不能超过硬限制,软限制可以有普通用户修改,而硬限制只能由 root 用户修改。可选值如下
  • soft :软限制
  • hard :硬限制
  • - :软限制和硬限制
  • <item> :资源项
  • core - limits the core file size (KB)
  • data - max data size (KB)
  • fsize - maximum filesize (KB)
  • memlock - max locked-in-memory address space (KB)
  • nofile - max number of open file descriptors
  • rss - max resident set size (KB)
  • stack - max stack size (KB)
  • cpu - max CPU time (MIN)
  • nproc - max number of processes
  • as - address space limit (KB)
  • maxlogins - max number of logins for this user
  • maxsyslogins - max number of logins on the system
  • priority - the priority to run user process with
  • locks - max number of file locks the user can hold
  • sigpending - max number of pending signals
  • msgqueue - max memory used by POSIX message queues (bytes)
  • nice - max nice priority allowed to raise to values: [-20, 19]
  • rtprio - max realtime priority
  • <value> :可以由无限制或无限大,或具体的值。priority and nice 不能使用 unlimited
  • unlimited : 无限制
  • 其它数值
  • 示例
*               soft    core            0
*               hard    nofile          512
@student        hard    nproc           20
@faculty        soft    nproc           20
@faculty        hard    nproc           50
ftp             hard    nproc           0
@student        -       maxlogins       4
:123            hard    cpu             5000
@500:           soft    cpu             10000
600:700         hard    locks           10