背景知识:
x86_64体系结构的通用寄存器
-
32位x86的通用寄存器:8个通用寄存器
-
eax 一般用作累加器(add)
-
ebx 一般作为基地址寄存器(base)
-
ecx 一般作为计数寄存器(count)
-
edx 一般作为存放数据(data)
-
esp 一般作为栈指针寄存器(stack pointer)
-
ebp 一般作为基指针寄存器(base pointer)
-
esi 一般作为源变地址寄存器(source index)
-
edi 一般作为目标地址寄存器(destinatin index)
-
-
x86_64通用寄存器:
-
扩展到16个: %rax, %rbx, %rcx, %rdx, %rdi, %rsi, %rsp, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15
-
把原来的e开头变成r开头,原来通用的e开头寄存器依然可用,表示低32位
-
16个寄存器:
-
%rax 作为函数返回值使用。
-
%rsp栈指针寄存器,指向栈顶
-
%rdi, %rsi, %rdx, %rcx, %r8, %r9用作函数参数,依次对应第1参数,第2个参数....
-
%rbx, %rbp, %r12, %r14, %r15用作通用寄存器,数据存储
-
%r10, %r11用作通用寄存器
-
-
%rbp是栈框指针
-
mov指令和lea指令的区别:
-
lea指令:load effective address。一般翻译为地址传送指令。
-
lea指令例子:leal -8(%ebp), %eax
-
取出ebp寄存器的值并减去8赋值给eax寄存器,ebp-8 -> eax
-
不进行间接寻址,直接传送地址
-
-
movl -8(%ebp), %eax
-
取出ebp的值减去8,然后再读取(ebp-8)所指向内存的内容,赋给eax,
-
进行了间接寻址取出变量值(内容),[ebp - 8] -> eax
-
栈结构
-
函数的调用与栈有着密切的关系,程序的执行通常是一个函数嵌套着下一个函数,无论嵌套有多深,程序总是能正确的返回到原点,这个就要依赖于栈的结构、rsp栈指针寄存器以及rbp栈基地址寄存器
-
假设函数调用关系如下:main()->func1()->func2()
-
pop和push指令
-
push 首先将rsp值减8,然后在将操作数内容压入rsp所指向的位置。
-
pop 首先将rsp所指示的地址中的内容出栈,然后将rsp的值加8
-
例子:stack.c
unsigned long func2(unsigned long f2)
{
unsigned long i2 = 2;
f2 += i2;
return f2;
}
unsigned long func1(unsigned long f1)
{
unsigned long i1 = 1;
f1+=i1;
f1 = func2(f1);
return f1;
}
int main()
{
unsigned long m = func1(0);
return m;
}
gcc stack.c
objdump -S a.out > stack.S
stack.S
00000000004004d6 <func2>:
4004d6: 55 push %rbp
4004d7: 48 89 e5 mov %rsp,%rbp
4004da: 48 89 7d e8 mov %rdi,-0x18(%rbp)
4004de: 48 c7 45 f8 02 00 00 movq $0x2,-0x8(%rbp)
4004e5: 00
4004e6: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004ea: 48 01 45 e8 add %rax,-0x18(%rbp)
4004ee: 48 8b 45 e8 mov -0x18(%rbp),%rax
4004f2: 5d pop %rbp
4004f3: c3 retq
00000000004004f4 <func1>:
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 48 83 ec 18 sub $0x18,%rsp
4004fc: 48 89 7d e8 mov %rdi,-0x18(%rbp)
400500: 48 c7 45 f8 01 00 00 movq $0x1,-0x8(%rbp)
400507: 00
400508: 48 8b 45 f8 mov -0x8(%rbp),%rax
40050c: 48 01 45 e8 add %rax,-0x18(%rbp)
400510: 48 8b 45 e8 mov -0x18(%rbp),%rax
400514: 48 89 c7 mov %rax,%rdi
400517: e8 ba ff ff ff callq 4004d6 <func2>
40051c: 48 89 45 e8 mov %rax,-0x18(%rbp)
400520: 48 8b 45 e8 mov -0x18(%rbp),%rax
400524: c9 leaveq
400525: c3 retq
0000000000400526 <main>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: 48 83 ec 10 sub $0x10,%rsp
40052e: bf 00 00 00 00 mov $0x0,%edi
400533: e8 bc ff ff ff callq 4004f4 <func1>
400538: 48 89 45 f8 mov %rax,-0x8(%rbp)
40053c: 48 8b 45 f8 mov -0x8(%rbp),%rax
400540: c9 leaveq
400541: c3 retq
400542: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
400549: 00 00 00
40054c: 0f 1f 40 00 nopl 0x0(%rax)
假如栈开始的地址为0x1000
如何触发dump
kdump通常用于系统假死机(unresposive)和panic,也就是没有响应的情况。硬件问题导致的直接死机,kdump无能为力。
kdump触发条件有哪些?
1.手动触发
SysRq echo c > /proc/sysrq-trigger
NMI via IPMI ipmitool power diag
NMI via virsh virsh inject-nmi MyGuestName
Beware of Kernel.unkown_nmi_panic=1
2.自动触发
Watchdog Boot cmdline: nmi_watchdog=1
Softlockup sysctl kernel.softlockup_panic=1
Out of Memory sysctl vm.panic_on_oom=1
实验0:安装kdump和crash
1.工具安装
yum install kernel-debuginfo kexec-tools crash
2.设置crashkernel预留内存大小,修改/etc/default/grub文件
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.lvm.lv=cl/root rd.lvm.lv=cl/swap crashkernel=400M"
GRUB_DISABLE_RECOVERY="true"
3.需要重新生成grub配置文件,重启系统才能生效
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
4.开启kdump服务
systemctl start kdump.service //启动kdump
systemctl enable kdump.service //设置开机启动
5.检查kdump服务时否开启?
# service kdump status
Redirecting to /bin/systemctl status kdump.service
● kdump.service - Crash recovery kernel arming
Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled; vendor preset: enabled)
Active: active (exited) since Fri 2021-06-18 04:44:54 EDT; 45min ago
Main PID: 991 (code=exited, status=0/SUCCESS)
CGroup: /system.slice/kdump.service
Jun 18 04:44:54 localhost.localdomain dracut[2984]: drwxr-xr-x 2 root root 0 Jun 18 04:44 usr/share/zoneinfo/America
Jun 18 04:44:54 localhost.localdomain dracut[2984]: -rw-r--r-- 1 root root 3519 Mar 27 2017 usr/share/zoneinfo/America/New_York
Jun 18 04:44:54 localhost.localdomain dracut[2984]: drwxr-xr-x 2 root root 0 Jun 18 04:44 var
Jun 18 04:44:54 localhost.localdomain dracut[2984]: lrwxrwxrwx 1 root root 11 Jun 18 04:44 var/lock -> ../run/lock
Jun 18 04:44:54 localhost.localdomain dracut[2984]: lrwxrwxrwx 1 root root 6 Jun 18 04:44 var/run -> ../run
Jun 18 04:44:54 localhost.localdomain dracut[2984]: ========================================================================
Jun 18 04:44:54 localhost.localdomain dracut[2984]: *** Creating initramfs image file '/boot/initramfs-3.10.0-957.1.3.el7.x86_64kdump.img' done ***
Jun 18 04:44:54 localhost.localdomain kdumpctl[991]: kexec: loaded kdump kernel
Jun 18 04:44:54 localhost.localdomain kdumpctl[991]: Starting kdump: [OK]
Jun 18 04:44:54 localhost.localdomain systemd[1]: Started Crash recovery kernel arming.
6.手动触发一下crash dump
echo 1 >/proc/sys/kernel/sysrq; echo c > /proc/sysrq-trigger
系统会自动重启,重启后可以看到在/var/crash/目录下生成了coredump文件
打开crash来分析:
# crash vmcore /usr/lib/debug/lib/modules/3.10.0-957.1.3.el7.x86_64/vmlinux
crash 7.2.3-8.el7
Copyright (C) 2002-2017 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions.
This program has absolutely no warranty. Enter "help warranty" for details.
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu"...
WARNING: kernel relocated [126MB]: patching 85619 gdb minimal_symbol values
KERNEL: /usr/lib/debug/lib/modules/3.10.0-957.1.3.el7.x86_64/vmlinux
DUMPFILE: vmcore [PARTIAL DUMP]
CPUS: 4
DATE: Fri Jun 18 05:32:32 2021
UPTIME: 00:47:57
LOAD AVERAGE: 0.00, 0.01, 0.05
TASKS: 413
NODENAME: localhost.localdomain
RELEASE: 3.10.0-957.1.3.el7.x86_64
VERSION: #1 SMP Thu Nov 29 14:49:43 UTC 2018
MACHINE: x86_64 (3799 Mhz)
MEMORY: 2 GB
PANIC: "SysRq : Trigger a crash"
PID: 12653
COMMAND: "bash"
TASK: ffffa1071aca8000 [THREAD_INFO: ffffa1074b32c000]
CPU: 3
STATE: TASK_RUNNING (SYSRQ)
crash>
crash支持的命令:
crash> help
* extend log rd task
alias files mach repeat timer
ascii foreach mod runq tree
bpf fuser mount search union
bt gdb net set vm
btop help p sig vtop
dev ipcs ps struct waitq
dis irq pte swap whatis
eval kmem ptob sym wr
exit list ptov sys q
查看某个命令的详细介绍:
help cmd
-
bt命令:输出内核栈的backtrace
-
-t 显示符号信息
-
-f 显示栈的所有数据
-
-l 显示文件名和行号
-
pid 可以显示指定pid进程的backtrace
-
-
dis 显示反汇编
-
-l 显示反汇编以及源代码
-
-s 显示该函数的文件名和源代码
-
-
mod 加载模块符号表
-
-s 加载模块符号表
-
-d 删除某个模块符号表
-
-S 加载某个目录的模块符号表
-
-
sym 用来解析符号(symbol)
-
-l 显示所有的符号,等用于System.map
-
-m modules: 显示指定模块里的符号
-
-q string: 查找符号
-
-
rd 读内存
-
-p 读物理地址
-
-u 读用户虚拟地址
-
-d 显示10进制
-
-s 显示符号
-
-32 显示32位宽的值
-
-64 显示64位宽的值
-
-a 显示ASCII码
-
-
struct 显示数据结构的定义以及实际的值
-
struct_name: 显示内核中定义的数据结构
-
.member 显示数据结构中某个成员
-
struct_name address: 显示该数据结构在该地址的值
-
-o: 显示每个成员在数据结构中的偏移(十进制)
-
-
p命令:用来打印一个表达式或符号的值
-
symbol: 打印一个内核的符号的值,通常是expose出来的全局变量等。
-
symbol:n :打印某个cpu上pre-cpu变量的值
-
-
task: 进程的task_struct和thread_info数据结构的值
-
runq命令:显示每个CPU的就绪队列的信息
-
-t: 显示就绪队列中的时间戳
-
-m: 显示当前进程运行的时间
-
-
vm命令:显示指定进程的相关内存的信息
-
-p: 显示虚拟地址和物理地址
-
-m: 显示mm_struct数据结构的值
-
-v: dump该进程所有vm_area_struct数据结构的值
-
-f 1234:数字1234在vm_flags的对应比特位
-
-
kmem命令:现在系统内存信息
-
-i: 显示系统内存使用的情况
-
-v: 显示系统vmalloc使用情况
-
-V: 显示系统vm_start情况
-
-z: 显示每个zone的情况
-
-s: 显示slab情况
-
-p: 显示每个页面的情况
-
-g: 显示struct page里面flag的比特位
-
-
list命令:用来遍历链表,并且打印链表中的成员的值
-
-h: 指定链表头list_head的地址
-
-s: 用来打印链表成员的值
-