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