来源:公众号【编程珠玑】

作者:守望先生

ID:shouwangxiansheng

拿到一个编译好的可执行文件,你能获取到哪些信息?文件大小,修改时间?文件类型?除此之外呢?实际上它包含了很多信息,这些你都知道吗?

示例程序

//main.c
#include<stdio.h>
void testFun()
{printf("公众号:编程珠玑\n");
}
int main(void)
{testFun();return 0;
}

编译得到可执行文件main:

$ gcc -o main main.c

ELF头信息

只需要一条简单的命令,就可以获取很多信息

$ readelf -h main
ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              EXEC (Executable file)Machine:                           Advanced Micro Devices X86-64Version:                           0x1Entry point address:               0x400430Start of program headers:          64 (bytes into file)Start of section headers:          6648 (bytes into file)Flags:                             0x0Size of this header:               64 (bytes)Size of program headers:           56 (bytes)Number of program headers:         9Size of section headers:           64 (bytes)Number of section headers:         31Section 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平台的。

同样的,当你在交叉编译的时候,发现总有一个库链接不上,但是库又存在,不妨看看这个库和你要编译的平台是否匹配。

链接了哪些动态库?

编好的程序依赖了哪些动态库呢?可不要放到另外一个平台就起不来啊。瞅瞅:

$ ldd mainlinux-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找不到,不妨看看它链接的动态库在什么位置,你的机器上到底有没有吧。

新增的函数和全局变量包含了吗?

新增了一个全局变量或者函数,但是编译完之后,不确定有没有?

$ nm main |grep testFun
0000000000400526 T testFun

nm看下就知道了。当然了,如果你看到某个库的函数前面的标志不是T,而是U,说明该函数未在该库中定义。

nm主要用于查看elf文件的符号表信息。

有符号表吗

我们都知道,没有符号表的程序,在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

程序占用空间太大?

为什么程序的占用空间这么大?不妨看看是不是使用了过多的静态变量或全局变量:

$ size maintext       data     bss     dec     hex filename1261        552       8    1821     71d main

看到data部分的大小了吗?看起来并没有多少,如果这里占用空间过大,那可能是你程序中用到了太多的全局变量和静态变量或常量。当然了,如果你的全局变量都是初始化为0的,那么data这里是不会有明显的变化的(为什么?)。

在开头分别加下面这一行,其影响可执行文件的效果不一样奥。

char str[1000] = {0};
char str[1000] = {1};

包含某个字符串吗

这个程序里面包含什么特殊的字符串吗?可以搜索一下:

$ strings main |grep hello
hello,

嗯?这样一想,好像还可以把版本号信息写进去呢。

C还是C++?

如果将前面的程序按照C++编译:

$ g++ -o main main.c
$ nm main |grep test
0000000000400526 T _Z7testFunv

你会发现使用g++编译出来的test函数符号前带头,后带尾,这也是C++中有重载和C中没有重载的原因之一。

函数的汇编代码是?

反汇编所有代码:

$ objdump -d main

那如果要反汇编特定函数(如main函数)呢?先按照地址顺序输出符号表信息:

$ nm -n main |grep main -A 1
0000000000400537 T main
0000000000400550 T __libc_csu_init

我们得到main的开始地址为0x400537,结束地址为0x400550。
反汇编:

$ objdump -d main --start-address=0x400537 --stop-address=0x400550
0000000000400537 <main>:400537:    55                      push   %rbp400538:    48 89 e5                mov    %rsp,%rbp40053b:    b8 00 00 00 00          mov    $0x0,%eax400540:    e8 e1 ff ff ff          callq  400526 <testFun>400545:    b8 00 00 00 00          mov    $0x0,%eax40054a:    5d                      pop    %rbp40054b:    c3                      retq   40054c:    0f 1f 40 00             nopl   0x0(%rax)

看看只读数据区有哪些内容?

当我们尝试修改常量字符串的时候,编译器会提示我们,它们是只读的,真的如此吗?

$ readelf main -x .rodata
Hex dump of section '.rodata':0x004005d0 01000200 00000000 68656c6c 6f2ce585 ........hello,..0x004005e0 ace4bc97 e58fb7ef bc9ae7bc 96e7a88b ................0x004005f0 e78fa0e7 8e9100                     .......

看到了吗?我们的hello,字符串放在了这里。

总结

本文仅列出了一些比较常见的可执行文中能读到的信息,欢迎补充。

思考

对于a和b,它们的内存存储区域是一样的吗?为什么?

char *a = "hello,world";
char a[] = "hello,world";

sizeof计算a和b的大小一样吗?又为什么?

万万没想到,一个可执行文件原来包含了这么多信息!相关推荐

  1. 万万没想到,一个技术方案帮实习生追到了运营妹子!

    上回说到,公司的新业务增长速度放缓,运营部门提出要发展短视频来促进更快的业务增长,而我也因为提前准备好了技术预案再一次得到老板的赞赏(了解详情请看上集:一个技术预案,让老板当场喊出了奥利给 ). 既然 ...

  2. 2015年第21本:万万没想到,用理工科思维理解世界

    <万万没想到:用理工科思维理解世界>这本书好像是从amazon排行榜中发现的,先是下载了电子版,竟然是一个博客(学而时嘻之)的大合集,可能是网上的某人用作者的博客制作而成的,共有123章! ...

  3. 6月Top 20榜单出炉啦! 万万没想到区块链大佬竟在忙这个...

    6月Top 20榜单出炉啦! 万万没想到区块链大佬竟在忙这个... 一个项目在 GitHub 上的活跃指数,在一定程度上代表了这个项目的开发状态. 频繁更新代码的项目有可能正处于构建和完善中,而停止更 ...

  4. 字节跳动---万万没想到之抓捕孔连顺

    字节跳动-万万没想到之抓捕孔连顺 文章目录 字节跳动---万万没想到之抓捕孔连顺 一.题目描述 二.分析 三.代码 一.题目描述 我叫王大锤,是一名特工.我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐 ...

  5. 牛客网-数据结构笔试题目(二)-万万没想到之抓捕孔连顺思路解析(附源码)

    题意 我叫王大锤,是一名特工.我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺.和我一起行动的还有另外两名特工,我提议 我们在字节跳动大街的N个建筑中选定3个埋伏地点. 为了相互照应,我们决 ...

  6. 万万没想到之抓捕孔连顺

    万万没想到之抓捕孔连顺 题目描述 我叫王大锤,是一名特工.我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺.和我一起行动的还有另外两名特工,我提议 我们在字节跳动大街的N个建筑中选定3个埋伏 ...

  7. 我是实小楼,万万没想到,强化学习帮我走出了迷宫

    (文末有福利哦) 我叫实小楼 万万没想到 我被困在迷宫里走不出来了-- 看到图中身处迷宫入口 一脸懵逼的小狮子没? 那就是我 今天老板把我扔在了这儿 说是要惩罚我 "走不出迷宫, 实验楼就准 ...

  8. 万万没想到之抓捕孔连顺问题的一些思考

    我叫王大锤,是一名特工.我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺.和我一起行动的还有另外两名特工,我提议我们在字节跳动大街的N个建筑中选定3个埋伏地点.为了相互照应,我们决定相距最远 ...

  9. w万万没想到之抓特工

    我叫王大锤,是一名特工.我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺.和我一起行动的还有另外两名特工,我提议 我们在字节跳动大街的N个建筑中选定3个埋伏地点. 为了相互照应,我们决定相距 ...

最新文章

  1. md5sum命令详解
  2. Android eMMC 分区详解(转载)
  3. openjdk(HOTSPOT)垃圾回收源码解读
  4. 浙大计算机学院朱建科,浙江大学计算机科学与技术学院导师介绍:朱建科
  5. Spring Insight – Web应用程序分析
  6. LeetCode MySQL 1084. 销售分析III
  7. java语言特点解释类_Java语言特点
  8. 为什么你一直在写bug?原因找到了
  9. 【Java】计算1+1/2+1/3+1/4....前20项
  10. 线性表11|单链表小结:腾讯面试题 - 数据结构和算法16
  11. 史上最全近百条Oracle DBA日常维护SQL脚本指令
  12. java 注释工具栏_eclipse/intellij idea 查看java源码和注释方法
  13. 偏微分方程数值解---学习总结(2)
  14. 模块化存储区域网络(SAN)解决方案的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  15. Word | 添加图题/图注、插入题注、设置插入题注快捷键...
  16. 怎么把音乐WAV格式转换为MP3格式
  17. imazing iOS设备管理软件
  18. Excel - 字符串处理函数:LEFT, RIGHT, MID, LEN 和 FIND
  19. Java经典代码工具类2
  20. SAP ABAP 小数转分数\约分

热门文章

  1. 大话设计模式(十三 有了门面,程序员的程序会更加体面!)
  2. FTServer 1.1 发布,多语言全文搜索服务器
  3. 3分钟学会Mysql查询之表连接,左连接,右连接,内连接…
  4. Android快速开发框架-ZBLibrary 源码分享
  5. 天气预报开放API调用
  6. Mac OS 错误代码 -8072的可行解决方法
  7. Java - 为什么要使用接口编程【转】
  8. c++ socket启动网卡接口
  9. Fabric架构演变之路
  10. python如何获取版本信息判断当前的python版本