0%

词汇表(Glossary)

  • BAR: base address register.
  • PCI configuration space.
  • sba: system bus address.

PCI

PCI configurtation space

PCI设备有一组被称为configuratin space的寄存器,并且PCI Express引入了Extended configuration space。configuration space寄存器被映射到memory locations。
驱动设备和诊断软件必须要有权限访问configuration space,操作系统通常使用APIs来授权访问设备configuration space。

PCI configuration space有两种类型的Header(64 bytes):

Header Type 0x0

Register Offset Bits 31-24 Bits 23-16 Bits 15-8 Bits 7-0
0x0 0x0 Device ID Vendor ID
0x1 0x4 Status Command
0x2 0x8 Class code Subclass Prog IF Revision ID
0x3 0xC BIST Header type Latency Timer Cache Line Size
0x4 0x10 Base address #0 (BAR0)
0x5 0x14 Base address #1 (BAR1)
0x6 0x18 Secondary Latency Timer Subordinate Bus Number Secondary Bus Number Primary Bus Number
0x7 0x1C Secondary Status I/O Limit I/O Base
0x8 0x20 Memory Limit Memory Base
0x9 0x24 Prefetchable Memory Limit Prefetchable Memory Base
0xA 0x28 Prefetchable Base Upper 32 Bits
0xB 0x2C Prefetchable Limit Upper 32 Bits
0xC 0x30 I/O Limit Upper 16 Bits I/O Base Upper 16 Bits
0xD 0x34 Reserved Capability Pointer
0xE 0x38 Expansion ROM base address
0xF 0x3C Bridge Control Interrupt PIN Interrupt Line

Header Type 0x1

Register Offset Bits 31-24 Bits 23-16 Bits 15-8 Bits 7-0
0x0 0x0 Device ID Vendor ID
0x1 0x4 Status Command
0x2 0x8 Class code Subclass Prog IF Revision ID
0x3 0xC BIST Header type Latency Timer Cache Line Size
0x4 0x10 Base address #0 (BAR0)
0x5 0x14 Base address #1 (BAR1)
0x6 0x18 Secondary Latency Timer Subordinate Bus Number Secondary Bus Number Primary Bus Number
0x7 0x1C Secondary Status I/O Limit I/O Base
0x8 0x20 Memory Limit Memory Base
0x9 0x24 Prefetchable Memory Limit Prefetchable Memory Base
0xA 0x28 Prefetchable Base Upper 32 Bits
0xB 0x2C Prefetchable Limit Upper 32 Bits
0xC 0x30 I/O Limit Upper 16 Bits I/O Base Upper 16 Bits
0xD 0x34 Reserved Capability Pointer
0xE 0x38 Expansion ROM base address
0xF 0x3C Bridge Control Interrupt PIN Interrupt Line
BAR0
Memory-mapped I/O (MMIO) registers
BAR1
Device memory windows.
BAR2/3
Complementary space of BAR1.
BAR5
I/O port.
BAR6
PCI ROM.

Commands

lspci

Interpreting the output of lspci

参考

简介

LDAP, Lightweight Directory Access Protocol.

参考

Bull Support

驱动

本文介绍驱动。

命令

  • lsmod - Show status of modules in the Linux kernel.

    lsmod is a trivial program which nicely formats the contents of the /proc/odules,
    showing what kernel modules are currently loaded.

  • dmesg - examine or control the kernel ring buffer.

    The kernel buffer is a data structure used for keeping the log messages of the kernel and the kernel modules.
    It’s a ring buffer with a fixed size. Once it’s full, new messages overwrite the oldest messages.
    During boot, the kernel saves the messages into the kernel buffer.

  • lsscsi - Uses information in sysfs (Linux kernel series 2.6 and later) to list SCSI devices (or hosts) currently attached to the system.

gitignore不生效

参考:链接

file mode 10064的含义

参考博客

环境变量

  • environ - 用户环境,一个全局变量。头文件 <unistd.h>。可以通过 man environ 查看手册。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <unistd.h>
    #include <iostream>
    using namespace std;

    int main() {
    cout << "program environment: " << endl;
    for (char** entry = environ; *entry; ++entry) {
    cout << *entry << endl;
    }
    }
  • getenv - 获取环境变量。头文件 <stdlib.h>

  • setenv - 设置环境变量。头文件 stdlib.h

gdb 实现原理

参考 链接

在 vim / emacs 中启动 gdb

为什么在 Emacs/vim 中运行 GDB?

  • 统一环境:你可以在 Emacs 中同时查看源代码和调试信息
  • 快捷键支持:使用 Emacs 的快捷键控制 GDB(如设置断点、单步执行)
  • 图形化布局:支持多窗口显示,如断点列表、堆栈信息、变量值等
  • 增强效率:无需离开编辑器即可完成调试任务

Vim 从 8.1 版本开始内置了一个叫做 Termdebug 的插件,它可以直接在 Vim 中运行 GDB,并显示调试信息。

✅ 步骤:
确保 Vim 版本 ≥ 8.1

编译你的程序(加上 -g 选项):

1
gcc -g my_program.c -o my_program

在 Vim 中加载插件:

1
2
:packadd termdebug
:Termdebug

Vim 会打开一个新的窗口,显示 GDB 控制台,你可以在里面输入 GDB 命令。

📖 教程参考:Baeldung 的 Vim-GDB 集成指南

gdb 命令

  • thread apply [threadno] [all] args - 将命令传递给一个或多个线程,参见 链接
    比如,thread apply all continue 表示将 continue 命令传递给所有线程,也就是让所有线程都继续运行。

  • rbreak - Set a breakpoint for all functions matching REGEXP. 参考 链接

    e.g. rbreak file.C:.* - 给 file.C 的所有函数加上断点。

  • info

    • info inferior - 可以查看当前调试的进程的 PID。另外一种方法是在 gdb 命令行中直接调用 C 函数:print (int)getpid()。参考:链接
    • info source - 当前调试的源文件路径。
    • info sources <pattern> - 查询源文件路径
    • info proc - 当前进程信息
      • info proc files - 当前进程打开的文件(和文件描述符)。
      • info args - 查看函数参数
      • info locals - 查看局部变量。
      • info reg 查看寄存器。

常用寄存器:

寄存器 用途说明
rax 函数返回值,乘除法运算
rbx 通用寄存器,常用于基址
rcx 循环计数器
rdx I/O 指针或中间数据
rdi 函数第一个参数
rsi 函数第二个参数
rsp 栈顶指针
rbp 栈底指针,栈帧基址
rip 当前执行指令地址
eflags 运算状态标志,如 ZF(零标志)、CF(进位标志)等

gdb 的寄存器变量

x86(32 位)

$eax, $ebx, $ecx, $edx 通用寄存器
$esi, $edi 源 / 目标索引寄存器
$esp 栈顶指针(Stack Pointer)
$ebp 栈底指针(Base Pointer)
$eip 指令指针(下一条执行的指令地址)
$eflags 标志寄存器(保存条件标志)

x86_64(64 位)

寄存器 说明
$rax, $rbx, $rcx, $rdx 通用寄存器(64 位)
$rsi, $rdi 参数寄存器
$rsp 栈顶指针
$rbp 栈底指针
$rip 指令指针
$r8~$r15 扩展通用寄存器
$eflags 标志寄存器
  • show
    • show environment 查看全局变量
    • set environment <var>=<value> 设置环境变量
  • attach - 连接到正在运行的进程。与 gdb -p 效果相同。
  • detach - 取消连接的进程。
  • handle <signal> print pass nostop - 捕获信号(比如 SIGSEGV)并且忽略它。handle <signal nostop
    • pass 表示 gdb 会将捕获到的信号发回给被调试的进程。
  • set - 修改变量的值,比如 set x=10(或 set var x=10)将变量 x 的值改为 10。参考 博客
  • show directories
  • print - gdb 默认设置打印字符串的长度为 200;更改打印最大长度:set print elements <number-of-elements>0 表示 unlimited.
    - 打印数组: print arr[0]@3 ,其中 @3 表示打印 3 个元素。
    - 以十六进制打印:p/x <var>
  • ptype <variable name> - 打印变量类型。
  • finish - 从函数中返回,并打印函数返回值(即使函数的 return 语句很复杂,也可以获取返回值)。
  • frame <n> - 跳转到某个栈帧。
  • up 跳转到上一个栈帧
  • x/FMT: x 表示 examine ,查看内存。
    • /i 表示 instruction ,即查看汇编指令。
    • /g 表示 giant word ,即每次查看 8 字节。
      • x/g 0x400000 查看地址 0x400000 处的 8 字节内容(以十六进制显示)
      • x/4g $rsp 查看当前栈指针 $rsp 指向的连续 4 个 8 字节值(共 32 字节)
命令 说明
x/g 默认十六进制显示 8 字节内容
x/dg 十进制显示 8 字节内容
x/ug 无符号十进制显示 8 字节内容
x/tg 二进制显示 8 字节内容
x/fg 浮点数格式显示 8 字节内容(double)

环境变量

链接

断点

添加断点:

1
break file:line_no

查看断点:

1
info break

删除第 2 个断点:

1
delete 2

条件断点

参考:博客文档

break ... if cond

观察断点

捕捉断点

1
try...catch

打印长度的限制

  • Value sizes - 参考:文档
1
2
set max-value-size bytes
set max-value-size unlimited
  • 打印字符长度限制

    gdb 默认设置打印字符串的长度为 200;更改打印最大长度:set print elements

coredump

gdb 命令:gcore

Reference

WSL 无法使用 gdb

WSL 指 Windows 虚拟机。

解决方法

安装 PPA 的 daily build 版本

1
2
3
sudo add-apt-repository ppa:ubuntu-support-team/gdb
sudo apt update
sudo apt install gdb

gdb attach 权限报错

This is due to kernel hardening in Linux; you can disable this behavior by echo 0 > /proc/sys/kernel/yama/ptrace_scope or by modifying it in /etc/sysctl.d/10-ptrace.conf.

How to solve “ptrace operation not permitted” when trying to attach GDB to a process?

gdb debug forks

Reference

By default, when a program forks, gdb will continue to debug the parent process and the child process will run unimpeded.

If you want to follow the child process instead of the parent process, use the command set follow-fork-mode.

set follow-fork-mode mode
Set the debugger response to a program call of fork or vfork. A call to fork or vfork creates a new process. The mode argument can be:
parent
The original process is debugged after a fork. The child process runs unimpeded. This is the default.
child
The new process is debugged after a fork. The parent process runs unimpeded.
ask
gdb 会提示让你选择 parent 还是 child

show follow-fork-mode
Display the current debugger response to a fork or vfork call.
On Linux, if you want to debug both the parent and child processes, use the command set detach-on-fork.

set detach-on-fork mode
Tells gdb whether to detach one of the processes after a fork, or retain debugger control over them both.
on
The child process (or parent process, depending on the value of follow-fork-mode) will be detached and allowed to run independently. This is the default.
off
Both processes will be held under the control of gdb. One process (child or parent, depending on the value of follow-fork-mode) is debugged as usual, while the other is held suspended.

show detach-on-fork
Show whether detach-on-fork mode is on/off.

If you issue a run command to gdb after an exec call executes, the new target restarts. To restart the parent process, use the file command with the parent executable name as its argument. By default, after an exec call executes, gdb discards the symbols of the previous executable image. You can change this behaviour with the set follow-exec-mode command.

set follow-exec-mode mode
Set debugger response to a program call of exec. An exec call replaces the program image of a process.
follow-exec-mode can be:

new
gdb creates a new inferior and rebinds the process to this new inferior. The program the process was running before the exec call can be restarted afterwards by restarting the original inferior.
For example:

1
2
3
4
5
6
7
8
9
10
11
(gdb) info inferiors
(gdb) info inferior
Id Description Executable
* 1 <null> prog1
(gdb) run
process 12020 is executing new program: prog2
Program exited normally.
(gdb) info inferiors
Id Description Executable
* 2 <null> prog2
1 <null> prog1

same
gdb keeps the process bound to the same inferior. The new executable image replaces the previous executable loaded in the inferior. Restarting the inferior after the exec call, with e.g., the run command, restarts the executable the process was running after the exec call. This is the default mode.
For example:

1
2
3
4
5
6
7
8
9
(gdb) info inferiors
Id Description Executable
* 1 <null> prog1
(gdb) run
process 12020 is executing new program: prog2
Program exited normally.
(gdb) info inferiors
Id Description Executable
* 1 <null> prog2

Setting Catchpoints

Reference

gdb redirect to a log file

You need to enable logging:

1
2
3
4
5
6
7
(gdb) set logging on
Now GDB will log to ./gdb.txt. You can tell it which file to use:

(gdb) set logging file my_god_object.log
And you can examine the current logging configuration:

(gdb) show logging

更新:新版本 gdb 采用 set logging enabled on

记录输入的命令:

1
(gdb) set trace-commands on

Refercence

权限限制

1
2
$ cat /proc/sys/kernel/yama/ptrace_scope
3

Yama 是 Linux 内核中的一个 安全模块(LSM:Linux Security Module),专门用于加强进程间的访问控制,尤其是对 ptrace 系统调用的限制。
Yama 的主要目的是防止恶意程序通过 ptrace 附加到其他进程(包括 gdb, strace, pstack, gstack 等),从而窃取数据或注入代码。

模式名称 含义说明
0 经典模式 允许同一用户调试其权限范围内的任意进程(只要目标进程是 “可转储” 的)。适合开发环境。
1 受限模式(默认) 只允许调试直接子进程,或拥有 CAP_SYS_PTRACE 权限的进程。更安全,适合大多数系统。
2 管理员模式 只有 root 或具备 CAP_SYS_PTRACE 的进程可以使用 ptrace。适合高安全场景。
3 完全禁用 所有进程(包括 root)都无法使用 ptrace。彻底禁止调试行为,适合极端安全需求。

递归处理顺序

gcc 的输入文件和库是从左往右处理的。也就是说,以下命令是错误的:

1
gcc -L. -la main.cc

链接器处理到某个目标文件(如 main.cc 编译后的目标代码)时,如果遇到未解析的符号(比如 f() ),
它会从接下来的库中查找这些符号。因此顺序非常重要。

这里,-L. -la 选项在 main.cc 之前,链接器会首先尝试从 liba.so 中查找引用的符号,
但是,因为此时 main.cc 还未被处理,所以链接器还不知道有对 liba.so 中的函数 f() 的引用。
到了 main.cc ,链接器解析出引用,但它不会回头再去 liba.so 中查找,导致报错:”undefined reference to f()”。

正确的命令如下:

1
gcc main.cc -L. -la

注意:liba.so 的指定必须去掉 lib 和 .so ,也就是说不允许直接指定“库文件名”,而是只能指定“库名”。
如果想直接指定库文件名,那么应该把 liba.so 当成输入文件:

1
gcc main.cc ./liba.so

属性语法(Attribute Syntax)

参考:官方文档

函数属性(Function Attributes)

参考:

属性列举:

  • format (archetype, string-index, first-to-check)

    format 属性,指定函数采用 printf、scanf、strftime 或 strfmon 风格的参数,
    这些参数应根据格式字符串(format string)进行类型检查(type-checked)。
    类型检查发生在编译期。

    举例:

    1
    2
    3
    extern int
    my_printf (void *my_object, const char *my_format, ...)
    __attribute__ ((format (printf, 2, 3)));
    • archetype决定format string应该如何解释。
      可选为printfscanfstrftimestrfmon(也可以使用__printf____scanf____strftime____strfmon__)。
    • string-index指定哪个参数是format string(从1开始)。
    • first-to-check指定format string对应的第一个参数的序号。
      对于那些无法检查参数的函数(比如vprintf),该参数指定为0。在这种情况下,编译器仅检查format string的一致性。对于strftime格式,该参数必须为0

选项

  • -save-temps: 可以保留所有中间文件,例如预编译文件、汇编文件、目标文件等。

符号可见性

1
2
3
#pragma GCC visibility push(hidden)

__attribute__((__visibility__("default")))

gcc 编译选项:

1
gcc -fvisibility=hidden ...

ulimit

1
2
3
4
#查看配置
ulimit -a
#设置core file size
ulimit -c unlimited

ulimit只对当前终端有效。

以下两种方法对所有用户和终端有效:

  1. /etc/security/limits.conf中设置(redhat衍生系linux)。
  2. 或注释掉/etc/profile中的这一行:
    1
    2
    # No core files by default
    ulimit -S -c 0 > /dev/null 2>&1

core_pattern

core_pattern解释

链接

Read /usr/src/linux/Documentation/sysctl/kernel.txt.

core_pattern is used to specify a core dumpfile pattern name.

在系统启动时,Apport(crash reporting service)会生成配置文件/proc/sys/kernel/core_pattern。参考这里

Apport uses /proc/sys/kernel/core_pattern to directly pipe the core dump into apport:

1
2
3
$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport %p %s %c
$

Note that even if ulimit is set to disabled core files (by specyfing a core file size of zero using ulimit -c 0), apport will still capture the crash.

For intercepting Python crashes it installs a /etc/python*/sitecustomize.py to call apport on unhandled exceptions.

其中,/usr/share/apport/apport是一个python脚本。

以下是core_pattern文件的参数说明(参考Linux Manual Page:man core):

1
2
3
4
5
6
7
8
9
10
%c - Core file size soft resource limit of crashing process (since Linux 2.6.24).
%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 添加命令名

If the first character of the pattern is a '|', the kernel will treat the rest of the pattern as a command to run. The core dump will be written to the standard input of that program instead of to a file.

Apport的拦截组件默认是关闭的:

Apport itself is running at all times because it collects crash data for whoopsie (see ErrorTracker). However, the crash interception component is still disabled. To enable it permanently, do:

1
sudo nano /etc/apport/crashdb.conf

… and add a hash symbol # in the beginning of the following line:

'problem_types': ['Bug', 'Package'],

To disable crash reporting just remove the hash symbol.

设置core_pattern

链接

  1. /proc/sys/kernel/core_uses_pid可以控制产生的core文件的文件名中是否添加pid作为扩展,如果添加则文件内容为1,否则为0;

  2. /proc/sys/kernel/core_pattern可以设置格式化的core文件保存位置或文件名:

    1
    2
    3
    $ cat /proc/sys/kernel/core_pattern
    |/usr/share/apport/apport %p %s %c
    $ echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

    你可以用下列方式来完成:

    1
    2
    3
    4
    #查看所有sysctl所有变量的值。
    sysctl -a
    #设置变量kernel.core_pattern为如下值。
    sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t

    这些操作一旦计算机重启,则会丢失,如果你想持久化这些操作,可以在 /etc/sysctl.conf文件中增加:

    1
    kernel.core_pattern=/tmp/core%p

    加好后,如果你想不重启看看效果的话,则用下面的命令:

    1
    sysctl -p /etc/sysctl.conf

相关命令行工具

参考资料:

Linux Manual Page: man core

SEE ALSO
    bash(1),  coredumpctl(1),  gdb(1),  getrlimit(2), mmap(2), prctl(2), sigaction(2), elf(5), proc(5), pthreads(7), signal(7), systemd-coredump(8)

Segment Fault排查

参考链接

  1. dmesg + nm + addr2line

    addr2line只能找出executable的行号;如果是shared libraries,请使用gdb。参考这里

    dmesg输出的含义:

    ip: 表示instruction pointer.

  2. fprintf

  3. gdb

  4. signal(SIGSEGV,handler)

  5. valgrind
    参考:
    博客
    文档
    可以使用PostScript查看图形化结果

  6. heaptrack

    github仓库

  7. Jemalloc

    官网