一個LINUX可執行文件(可執行檔)原來包含了這麼多信息
一個LINUX可執行文件(可執行檔)原來包含了這麼多信息
00.測試程式碼
//main.c #include<stdio.h> void testFun() { printf("公众号:编程珠玑\n"); } int main(void) { testFun(); return 0; }
01.編譯成執行檔
$ gcc -o main main.c
02.讀取擋頭
$ readelf -h main 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: 0x400430 Start of program headers: 64 (bytes into file) Start of section headers: 6648 (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: 31 Section header string table index: 28
程序位數
Class: ELF64
Class展示了該程序的位數,如這裡顯示的是ELF64,如果你將它放到一個32位系統中運行,運行得起來就怪了。換句話說,64位系統上能運行32位和64位的程序,但是32位系統上,無法運行64位的程序。
大小端
Data: 2's complement, little endian
還記得那個到處可見的面試題嗎?如何判斷當前CPU是大端還是小端?除了各種秀代碼的方式,你想到這個方式了嗎?
找一個該平台上的正運行的可執行文件或系統庫,然後使用readelf -h看一下,是不是很快就看出來了?多麼明顯的little endian。
運行平台
Machine: Advanced Micro Devices X86-64
做嵌入式相關的可能經常需要做交叉編譯,而編譯出來的程序到底對不對呢?比如你在86平台編譯arm的程序,最終生成的可執行文件到底能不能運行在arm平台呢?通過Machine字段就可以很容易確定,從這裡可以看到,它是運行在x86平台的。
同樣的,當你在交叉編譯的時候,發現總有一個庫鏈接不上,但是庫又存在,不妨看看這個庫和你要編譯的平台是否匹配。
03.使用那些函示庫(函數庫)
$ ldd main linux-vdso.so.1 => (0x00007ffe750e7000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f749920a000) /lib64/ld-linux-x86-64.so.2 (0x00007f74995d4000)
04.有符號表嗎
我們都知道,沒有符號表的程序,在core之後是沒有太多有效信息可看的,也是無法使用gdb正常調試的,這個在《GDB調試入門,看這篇就夠了》中已經有提到了,那麼怎麼看有沒有符號表呢?
$ file main main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=0d9a7eb860459b585d2b33ae28d7c67d5ba12669, not stripped
如果使用file命令看到最後是not stripped,那麼則含有符號表,一般線上的程序可能會選擇去掉符號表信息,因為可以大大減少可執行文件的空間佔用。
$ strip main
這個時候再看看:
$ nm main no main symbols
05.程序佔用空間太大?
為什麼程序的佔用空間這麼大?不妨看看是不是使用了過多的靜態變量或全局變量:
$ size main
text data bss dec hex filename
1261 552 8 1821 71d main
看到data部分的大小了嗎?看起來並沒有多少,如果這裡佔用空間過大,那可能是你程序中用到了太多的全局變量和靜態變量或常量。當然了,如果你的全局變量都是初始化為0的,那麼data這裡是不會有明顯的變化的(為什麼?)。
在開頭分別加下面這一行,其影響可執行文件的效果不一樣奧。
char str[1000] = {0}; char str[1000] = {1};
06.C還是C++?
如果將前面的程序按照C++編譯:
$ g++ -o main main.c $ nm main |grep test 0000000000400526 T _Z7testFunv
你會發現使用g++編譯出來的test函數符號前帶頭,後帶尾,這也是C++中有重載和C中沒有重載的原因之一。