准备开个专栏,记录《从零开始实现链接器》的学习过程,先占个坑。
之前一直想把自己的学习过程记录在个人博客网站上,但这个要自己维护,上传图片什么的比较麻烦。关键是没有人互动,自己也没有怎么去看,慢慢的就遗忘了。在CSDN上有人点赞的时候我会看一下对应的博客,还能复习一下,索性后面的内容全部记录在CSDN上了。
学的过程中有一些零零散散的东西要记录,但可能没时间整理,索性先记一些关键词在这里,有空的时候在扩充和整理。所以我的博客可能经常不完整,像一个草稿,随记随保存。
参考链接:
静态链接和加载;最小动态加载器[南京大学(蒋炎岩)]
深入理解计算机系统——链接
从零开始实现链接器——第二课
https://en.wikipedia.org/wiki/GNU_Binutils
了解elf文件中的调试信息
有时候编译好程序调试的时候,虽然设置了断点,但程序并没有在断点的位置停下来。这可能是由于编译时没有创建调试信息。当使用g++
编译程序时,通过-g
参数可生成调试信息:
$ g++ -g elfinfo.cpp -o elfinfo
$ file elfinfo
elfinfo: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c814baea7a3040d5463c6a203dfb82c8a7f235b1, for GNU/Linux 3.2.0, with debug_info, not stripped
$ readelf -S elfinfo | grep debug[29] .debug_aranges PROGBITS 0000000000000000 00007043[30] .debug_info PROGBITS 0000000000000000 000075d3[31] .debug_abbrev PROGBITS 0000000000000000 00010cf5[32] .debug_line PROGBITS 0000000000000000 000118bb[33] .debug_str PROGBITS 0000000000000000 00012a7f[34] .debug_line_str PROGBITS 0000000000000000 0001cb79[35] .debug_rnglists PROGBITS 0000000000000000 0001cfa1
上面的命令先带-g
去编译程序,通过file
命令看到最后的信息为with debug_info, not stripped
,说明时存在调试信息的,这类文件可以被调试。如果用readelf -S elfinfo
查看Section Header Table里的信息,会发现存在一些debug段,调试器通过这些段来完成调试。如果不带-g
进行编译,则没有这些段,因此不能调试。
有时候为了进一步缩小文件尺寸,还会使用strip
命令删除文件中不重要的段,在strip后,通过file
命令输出的信息中会显示stripped
。当对比strip文件和未被strip的文件时,会发现strip后的文件少了两个段.symtab和.strtab。同时nm
命令的输出结果为空,如下:
$ g++ elfinfo.cpp -o elfinfo
$ nm -a ./elfinfo | wc186 507 11412
$ strip elfinfo
$ nm -a ./elfinfo | wc
nm: ./elfinfo: no symbols0 0 0
根据GPT的解释,nm的工作原理如下:
我:nm命令依赖于elf中的某些段吗?
GPT:是的,nm命令实际上依赖于ELF(Executable and Linkable Format)文件格式中的一些特殊段来获取信息。下面就是几个关键的部分:
- 符号表(Symbol Table): 符号表是存储程序中所有符号的信息的地方。这些信息包括符号的名称、类型、定义的位置等。nm命令主要从这个位置提取信息。
- 字符串表(String Table):这个部分用来储存符号表里符号的名称字符串。当我们使用nm命令时,它会读取这个部分来找到符号的名称。
- 符号表和字符串表的位置信息都存储在程序头表(Program Header Table)或者节区头部表(Section Header Table)中,nm命令会首先访问这些表,然后间接找到符号表和字符串表。
总的来说,nm命令不仅仅依赖于一个单独的段,而是从以上这些段中获取多个方面的信息来解析出符号的全貌。
RUST LD_PRELOAD