得到一个编译好的可执行文件,你能得到什么信息?文件大小,修改时间?文件类型?除此之外?其实它包含了很多信息,这些你都知道吗?示例程序//main.c#includevoidtestFun(){printf("公众号:ProgrammingPearls\n");}intmain(void){testFun();return0;}编译得到可执行文件filemain:$gcc-omainmain.cELFheaderinformation只需要一个简单的命令就可以得到很多信息$readelf-hmainELFHeader:Magic:7f454c46020101000000000000000000Class:ELF64Data:2'scomplement,littleendianVersion:1(current)OS/ABI:UNIX-SystemVABIVersion:0Type:EXEC(Executablefile)Machine:AdvancedMicroDevicesX86-64Version:0x1Entrypointaddress:0x400430Startofprogramheaders:64(bytesintofile)Startofsectionheaders:6648(bytesintofile)Flags:0x0Sizeofthisheader:64(bytes)Sizeofprogramheaders:56(bytes)Numberofprogramheaders:9Sizeofsectionheaders:64(bytes)Numberofsectionheaders:31Sectionheaderstringtableindex:28programdigitsClass:ELF64Class显示程序的位数,这里显示的是ELF64,如果放到32位系统运行会很奇怪。也就是说,32位和64位程序都可以在64位系统上运行,但是64位程序不能在32位系统上运行。大小端数据:2的补码,littleendian还记得那个随处可见的面试题吗?如何判断当前CPU是bigendian还是littleendian?除了各种展示代码的方式,你有没有想过这种方式?在这个平台上找一个正在运行的可执行文件或者系统库,然后用readelf-h看看能不能很快看到?多么明显的小端。运行平台Machine:AdvancedMicroDevicesX86-64嵌入式相关应用可能经常需要做交叉编译,但是编译出来的程序是否正确?比如你在86平台上编译一个arm程序,那么最终的可执行文件能不能在arm平台上运行呢?它可以通过Machine字段轻松确定。从这里可以看出它是在x86平台上运行的。同样,你在交叉编译的时候,发现总是有一个库链接不上,但是这个库是存在的。您不妨检查一下这个库是否与您要编译的平台匹配。链接了哪些动态库?编译后的程序依赖哪些动态库?不要放在其他平台上,它不会启动。看一看:$lddmainlinux-vdso.so.1=>(0x00007ffe750e7000)libc.so.6=>/lib/x86_64-linux-gnu/libc.so.6(0x00007f749920a000)/lib64/ld-linux-x86-64.so.2(0x00007f74995d4000)本来就是链接这些库的,所以当你从网上下载一些程序,运行的时候会提示有些so找不到。你不妨检查一下它链接的动态库在你机器上的位置有没有?是否包含新添加的函数和全局变量?添加了一个新的全局变量或函数,但是编译后不知道有没有?$nmmain|greptestFun0000000000400526TtestFunnm可以看到。当然,如果你看到某个库中某个函数前面的符号不是T,而是U,那就说明这个函数没有在这个库中定义。nm主要用来查看elf文件的符号表信息。有符号表吗?我们都知道,一个没有符号表的程序,在内核之后是没有多少有效信息可看的,用gdb是无法正常调试的。那么怎么看有没有符号表呢?$filemainmain:ELF64-bitLSBexecutable,x86-64,version1(SYSV),dynamicallylinked,interpreter/lib64/ld-linux-x86-64.so.2,forGNU/Linux2.6.32,BuildID[sha1]=0d9a7eb860459b585d2b33ae28d7c67d5ba12669,notstrippedHuh?能看到程序位数、适用平台等信息吗?如果你用file命令看到最后没有剥离,那么它包含一个符号表。一般在线程序可能会选择去掉符号表信息,因为这样可以大大减少可执行文件的数量。空间占用。此时查看$stripmain:$nmmainnomainsymbols程序占用空间太大?为什么程序占用这么多空间?看看是不是用了太多的静态变量或者全局变量:$sizemaintextdatabssdechexfilename12615528182171dmain看到data部分的size了吗??它看起来并不多。如果占用太多空间,可能是你在程序中使用了太多的全局变量和静态变量或常量。当然,如果你的全局变量都初始化为0,那么这里的数据不会有太大变化(为什么?)。在开头加上下面这行,对可执行文件的影响会有所不同。charstr[1000]={0};charstr[1000]={1};它包含某个字符串吗?该程序是否包含任何特殊字符串?你可以搜索:$stringsmain|grephellohello,嗯?这样一想,版本号信息好像也可以写进去。C还是C++?如果你按照C++编译前面的程序:$g++-omainmain.c$nmmain|greptest0000000000400526T_Z7testFunv,你会发现用g++编译的测试函数符号以head开头,以tail结尾。这也是C++中的重载和C。中没有过载的原因之一。函数的汇编代码是什么?反汇编所有代码:$objdump-dmain如果要反汇编特定函数(比如main函数)怎么办?首先按照地址顺序输出符号表信息:$nm-nmain|grepmain-A10000000000400537Tmain0000000000400550T__libc_csu_init我们得到main的起始地址为0x400537,结束地址为0x400550。反汇编:$objdump-dmain--start-405address=30x-stop-address=0x4005500000000000400537:400537:55push%rbp400538:4889e5mov%rsp,%rbp40053b:b800000000mov$0x0,%eax400540:e8e1ffffffcallq400526400545:b800000000mov$0x0,%eax40054a:5dpop%rbp40054b:c3retq40054c:0f1f4000nopl0x0(%rax)看看只读数据区里有什么?当我们尝试修改常量字符串时,编译器会提示我们它们是只读的,是这样吗?$readelfmain-x.rodataHexdumpofsection'.rodata':0x004005d0010002000000000068656c6c6f2ce585......你好,..0x004005e0ace4bc97e58fb7efbc9ae7bc96e7a88b.....................0x008e.9fa......见?我们的hello,string就放在这里。小结本文仅列出一些比较常见的可执行文本中可以阅读的信息,欢迎补充。想一想a和b,它们的内存存储区域是一样的吗?为什么?char*a="你好,世界";chara[]="你好,世界";sizeof计算出与a和b相同的大小?为什么?