Mengzelev's Blog

Lab3:链接与加载(2)实验报告

Word count: 1,268 / Reading time: 4 min
2018/11/24 Share

实现描述

寻找正确的入口地址

在框架代码抽取出的symtab中主项寻找,如果该项的Type属性为STT_FUNC,就根据其st_name属性去字符串表strtab里寻找该函数的Name属性,并与main进行对比,就能找到main函数对应的表项,此时该表项的st_value属性即 我们需要的入口地址。

加载程序

loader()函数的实现需要程序头表,模仿load_elf_table()函数的写法可将程序头表抽取到结构体Elf32_phdr中,找到p_typePT_LOAD的表项,读取其p_offsetp_vaddrp_fileszp_memsz属性,按照讲义的描述,使用fread函数将可执行文件中相对文件偏移p_offset的内容读取p_filedz到内存地址为[p_addr, p_addr + p_filesz的地方,并用memset函数将[p_vaddr + p_filesz, p_vaddr + p_memsiz)对应的物理区间清零。

打印栈帧链

第一次打印eip的值作为当前地址,之后每次都取内存中ebp-4的地址存放的返回地址作为函数调用的地址。函数的名字通过去symtab中寻找对应表项并从strtab中提取而得。仿照gdb的bt命令输出打印出来。对ebp的值解引用能得到上一个函数的ebp的值。如此循环直到ebp的信息为0为止。

实验过程

留着以后当笑话看的碎碎念

寻找正确的入口地址

我本来以为这个只要做好RTFSC就不会花太多时间的,man 5 elf之后理所当然地写了如下代码:

if(symtab[i].st_info == STT_FUNC)

发现怎么都找不到main,各种调试了一个多小时之后,问了同学才意识到,st_info不是直接的type值,type只占了低四位,高四位为绑定属性bind,把FM继续往下翻可以看到

ELF32_ST_BIND(info), ELF64_ST_BIND(info)                                                                    
Extract a binding from an st_info value. 

泪,可以流下来吗
论耐心RTFM的重要性

加载程序

这部分基本是照抄框架代码抽取El32_Shdr的方法抽取了Elf32_Phdr,让我自己写大概还是写不出来的吧[自卑.jpg]

打印栈帧连

先用gdb调试了给的segmentfault.c,摸索了一下bt命令的打印格式,确认了打印的栈帧链中的地址为函数调用时压入栈中的返回地址。

思考题

堆和栈在哪里?

经过PA3.2的调教,可以知道堆是程序运行时调用malloc动态向高地址申请空间维护的。堆区的开始位置就是数据段的结束位置。当调用malloc函数时,会触发系统调用sbrk查询并调整堆区的结束位置,即program break,以调整堆区的大小。
栈区是从内核虚存区开始向低地址方向生长的,栈顶的地址保存在寄存器esp中。通过pushpop等指令来调整栈区大小。
堆和栈的内容不放入可执行文件,我猜想一方面因为这些部分是动态生成的,程序执行过程中它们的大小时刻变化着,因此不能放入静态的可执行文件中。另一方面,栈区会有一个随机化操作来防止恶意攻击,不便于放入可读的可执行文件中。

如何识别不同格式的可执行文件?

ELF可执行目标文件有ELF头,根据ELF可执行文件的各种规范书写。Windows生成的可执行文件应该也有自己的文件头的格式,在GNU/Linux下执行时,系统按照ELF头的格式解析该文件的文件头出现了错误,例如没有读到“魔数”,就会报告“格式错误”。

消失的符号

因为这些符号只在本模块内被使用,其被动态分配在栈中,链接器并不需要这些符号的信息,因此不会出现在符号表中。符号是指参与链接的变量名和函数名。

寻找”Hello World!”

用上述方法找到hello程序的字符串表,会发现“Hello World!”字符串…它…根本不在字符串表中,而是在.rodata节中。

因为printf中的格式化字符串是一个只读的字符串,所以编译时会被放入只读数据节.rodata节中。

冗余的符号表

编译单独的hello程序没有问题,但是尝试对hello.o进行链接时就会链接报错(忘了截图了)。因为符号表用于链接中的符号解析,单独运行程序时没有符号表,单有指令就也没有问题。

冗余的属性?

起初我猜测是.bss节的数据没有在Filesz中被计算,而是计算在了Memsz中。但是我把.bss节的大小加上Filesz后还是比Memsz小一些,STFW发现stackoverflow上有人问了类似的问题,欣喜地点进去发现并没有人回答….我再思考一下说不定就有stackoverflow首答了…

为什么要清零?

数电和问求还没写完,溜了…….

CATALOG
  1. 1. 实现描述
    1. 1.1. 寻找正确的入口地址
    2. 1.2. 加载程序
    3. 1.3. 打印栈帧链
  2. 2. 实验过程
    1. 2.1. 寻找正确的入口地址
    2. 2.2. 加载程序
    3. 2.3. 打印栈帧连
  3. 3. 思考题
    1. 3.1. 堆和栈在哪里?
    2. 3.2. 如何识别不同格式的可执行文件?
    3. 3.3. 消失的符号
    4. 3.4. 寻找”Hello World!”
    5. 3.5. 冗余的符号表
    6. 3.6. 冗余的属性?
    7. 3.7. 为什么要清零?