本文列出在Linxu下帮助你调试或定位问题常用的命令,相信你一定会用到。本文旨在抛砖引玉,具体的使用详情可自行查找。
# file
Linux file命令用于查看文件类型。
# file /bin/ls
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=aaf05615b6c91d3cbb076af81aeff531c5d7dfd9, stripped
# file study
study: directory
# ldd
ldd的作用是打印可执行档依赖的共享库文件。针对动态链接的二进制文件运行它会显示其所有依赖的库及其路径。
$ ldd /bin/ls
linux-vdso.so.1 => (0x00007ffd623ef000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fd138066000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007fd137e61000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fd137c58000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd13788a000)
libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fd137628000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fd137424000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd13828d000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fd13721f000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd137003000)
# ltrace
ltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用,可以作为堆栈调用跟踪调试使用。
ltrace [option ...] [command [arg ...]]
-a 对齐具体某个列的返回值。
-c 计算时间和调用,并在程序退出时打印摘要。
-C 解码低级别名称(内核级)为用户级名称。
-d 打印调试信息。
-e 改变跟踪的事件。
-f 跟踪子进程。
-h 打印帮助信息。
-i 打印指令指针,当库调用时。
-l 只打印某个库中的调用。
-L 不打印库调用。
-n, --indent=NR 对每个调用级别嵌套以NR个空格进行缩进输出。
-o, --output=file 把输出定向到文件。
-p PID 附着在值为PID的进程号上进行ltrace。
-r 打印相对时间戳。
-s STRLEN 设置打印的字符串最大长度。
-S 显示系统调用。
-t, -tt, -ttt 打印绝对时间戳。
-T 输出每个调用过程的时间开销。
-u USERNAME 使用某个用户id或组ID来运行命令。
-V, --version 打印版本信息,然后退出。
# ltrace ./exe
__libc_start_main(0x40052d, 1, 0x7ffc38054de8, 0x400550 <unfinished ...>
puts("hello"hello
) = 6
+++ exited (status 0) +++
# hexdump
hexdump是Linux下的一个二进制文件查看工具,它可以将二进制文件转换为ASCII、八进制、十进制、十六进制格式进行查看。
$ hexdump /bin/ls |head
0000000 457f 464c 0102 0001 0000 0000 0000 0000
0000010 0003 003e 0001 0000 5850 0000 0000 0000
0000020 0040 0000 0000 0000 03a0 0002 0000 0000
0000030 0000 0000 0040 0038 0009 0040 001c 001b
0000040 0006 0000 0005 0000 0040 0000 0000 0000
0000050 0040 0000 0000 0000 0040 0000 0000 0000
0000060 01f8 0000 0000 0000 01f8 0000 0000 0000
0000070 0008 0000 0000 0000 0003 0000 0004 0000
0000080 0238 0000 0000 0000 0238 0000 0000 0000
0000090 0238 0000 0000 0000 001c 0000 0000 0000
# strings
strings命令 在对象文件或二进制文件中查找可打印的字符串。字符串是4个或更多可打印字符的任意序列,以换行符或空字符结束。strings命令对识别随机对象文件很有用。
语法:
strings [ -a ] [ - ] [ -o ] [ -t Format ] [ -n Number ] [ -Number ] [file ... ]
-a --all:扫描整个文件而不是只扫描目标文件初始化和装载段
-f –print-file-name:在显示字符串前先显示文件名
-n –bytes=[number]:找到并且输出所有NUL终止符序列
- :设置显示的最少的字符数,默认是4个字符
-t --radix={o,d,x} :输出字符的位置,基于八进制,十进制或者十六进制
-o :类似--radix=o
-T --target= :指定二进制文件格式
-e --encoding={s,S,b,l,B,L} :选择字符大小和排列顺序:s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit
@ :读取中选项
$ strings /bin/ls | head
/lib64/ld-linux-x86-64.so.2
libselinux.so.1
__gmon_start__
_init
fgetfilecon
freecon
lgetfilecon
_fini
libcap.so.2
cap_to_text
# readelf
一般用于查看ELF格式的文件信息,常见的文件如在Linux上的可执行文件,动态库(* .so)或者静态库(* .a) 等包含ELF格式的文件。
$ readelf -h exe
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400440
Start of program headers: 64 (bytes into file)
Start of section headers: 6432 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 29
# objdump
objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。
$ cat exe.c
#include <stdio.h>
int main()
{
printf("hello\n");
return 0;
}
$ objdump -S exe
...
000000000040052d <main>:
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
400531: bf e0 05 40 00 mov $0x4005e0,%edi
400536: e8 d5 fe ff ff callq 400410 <puts@plt>
40053b: b8 00 00 00 00 mov $0x0,%eax
400540: 5d pop %rbp
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)
...
# strace
strace常用来跟踪进程执行时的系统调用和所接收的信号。在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
$ strace ./exe
execve("./exe", ["./exe"], [/* 27 vars */]) = 0
brk(NULL) = 0xe22000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0e1780e000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=83540, ...}) = 0
mmap(NULL, 83540, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0e177f9000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0
mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0e17220000
mprotect(0x7f0e173e3000, 2097152, PROT_NONE) = 0
mmap(0x7f0e175e3000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f0e175e3000
mmap(0x7f0e175e9000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0e175e9000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0e177f8000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0e177f6000
arch_prctl(ARCH_SET_FS, 0x7f0e177f6740) = 0
mprotect(0x7f0e175e3000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f0e1780f000, 4096, PROT_READ) = 0
munmap(0x7f0e177f9000, 83540) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0e1780d000
write(1, "hello\n", 6hello
) = 6
exit_group(0) = ?
+++ exited with 0 +++
# nm
nm是name的缩写,它显示指定文件中的符号信息,文件可以是对象文件、可执行文件或对象文件库。如果文件中没有包含符号信息,nm报告该情况,但不把他解释为出错。nm缺省情况下报告十进制符号表示法下的数字值。
$ nm exe
0000000000601034 B __bss_start
0000000000601034 b completed.6355
0000000000601030 D __data_start
0000000000601030 W data_start
0000000000400470 t deregister_tm_clones
00000000004004e0 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
00000000004005d8 R __dso_handle
0000000000600e28 d _DYNAMIC
0000000000601034 D _edata
0000000000601038 B _end
00000000004005c4 T _fini
0000000000400500 t frame_dummy
0000000000600e10 t __frame_dummy_init_array_entry
0000000000400710 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
00000000004005e8 r __GNU_EH_FRAME_HDR
00000000004003e0 T _init
0000000000600e18 t __init_array_end
0000000000600e10 t __init_array_start
00000000004005d0 R _IO_stdin_used
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
00000000004005c0 T __libc_csu_fini
0000000000400550 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
000000000040052d T main
U puts@@GLIBC_2.2.5
00000000004004a0 t register_tm_clones
0000000000400440 T _start
0000000000601038 D __TMC_END__
...
# gdb
gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具。
$ gcc -g -o exe exe.c
$ gdb exe
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7
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-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/study/exe...done.
(gdb) list
1 #include <stdio.h>
2 int main()
3 {
4
5 printf("hello\n");
6 return 0;
7
8 }
(gdb) b 5
Breakpoint 1 at 0x400531: file exe.c, line 5.
(gdb) r
Starting program: /home/study/exe
Breakpoint 1, main () at exe.c:5
5 printf("hello\n");
(gdb) n
hello
6 return 0;
(gdb)