在Linux中,可執行文件的格式是ELF格式,而有一些命令可以幫助我們瞭解它們更多的“祕密”,以此來幫助我們解決問題。
示例程序
我們的示例程序如下:
//hello.c
#include<stdio.h>
int main(int argc,char *argv[])
{
printf("hello shouwangxiansheng\n");
return 0 ;
}
編譯,得到hello可執行文件。
$ gcc -o hello hello.c
查看文件類型
file命令可以用來查看文件類型:
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=8f1de0f59bdfe9aaff85ade6898173aa436b296a, not stripped
從結果中,我們可以知道,它是ELF可執行文件,且是64位程序,有動態鏈接,最後的not stripped也表明了它保留了符號表信息或者調試信息。
如果不是可執行文件,它的信息是怎樣的呢?舉個例子:
$ file hello.c
hello.c: C source, UTF-8 Unicode text
查看ELF頭
readelf用於查看ELF文件,如下所示:
$ readelf -h hello
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
可以看到它是EXEC,即可執行文件,且小端程序,運行於X86-64。在交叉編譯的時候,這個文件頭的信息也非常有用。例如你在x86的機器上交叉編譯出powerpc的可執行文件,在powerpc上卻不被識別,不能運行,不如用readelf看看它的Machine字段,是不是沒有編譯好。
查找ELF文件中的字符串
例如,你在文件中寫入了版本號或者特殊字符串,可以通過strings命令搜索到:
$ strings hello|grep shouwang
hello shouwangxiansheng
查看ELF文件各段大小
$ size hello
text data bss dec hex filename
1210 552 8 1770 6ea hello
這裏可以看到代碼段,數據段各自佔多少,必要時候還可以根據需要優化代碼,減少磁盤空間佔用。
查看鏈接的動態庫
運行時出現找不到動態庫?不如看看它鏈接了哪些庫吧:
$ ldd hello
linux-vdso.so.1 => (0x00007ffd16386000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f507e083000)
/lib64/ld-linux-x86-64.so.2 (0x00007f507e44d000)
可以看到它鏈接的動態庫是/lib/x86_64-linux-gnu/libc.so.6,而如果該文件不存在,則運行時將會出錯。這裏也可以參考《動態庫的製作和使用》。
查看符號表
新加的函數或者全局變量不知道有沒有編譯進去?如何看看符號表裏有沒有吧(前提是符號表沒有被去掉):
$ nm hello |grep main #符號表中查找main函數
U __libc_start_main@@GLIBC_2.2.5
0000000000400526 T main
如果沒有找到或者前面是U,沒有地址,表明在這個elf文件中沒有定義這個函數,鏈接出問題的時候很有用奧。
爲ELF文件瘦身
前面通過file查看文件時,看到有not stripped的字樣,由於它裏面包含了一些符號表信息,因爲文件會稍大,如果去掉,二進制文件將會變小,但是裏面的符號表信息也就沒有了,將會影響問題定位。
$ ls -lh hello #瘦身前
-rwxrwxr-x 1 root root 8.4K
$ strip hello
$ ls -lh hello #瘦身後
-rwxrwxr-x 1 root root 6.2K
可以看到,瘦身後二進制文件變得更小。當可執行文件越大時,瘦身效果就會更明顯了。當然放心,這不會影響程序的正常運行,只是對調試和問題定位有影響。
這個時候再看符號表:
$ nm hello
nm: hello: no symbols
打印文件校驗和
二進制文件傳輸過程中有沒有被損壞或者是否是同一個版本,看看校驗和以及程序塊計數吧:
$ sum hello
33513 7
當然你也可以使用:
$ md5sum hello
521efed706c3b485dd3b5e96e48b138a hello
來比對md5值。
總結
ELF文件中隱藏了豐富的信息,只要使用得當,將會幫助我們更好地進行開發或者問題的定位。